MTD core changes:

* Call of_platform_populate() for MTD partitions
 * Check devicetree alias for index
 * mtdoops:
   - Add a timestamp to the mtdoops header.
   - Create a header structure for the saved mtdoops.
   - Fix the size of the header read buffer.
 * mtdblock: Warn if opened on NAND
 * Bindings:
   - reserved-memory: Support MTD/block device
   - jedec,spi-nor: remove unneeded properties
   - Extend fixed-partitions binding
   - Add Sercomm (Suzhou) Corporation vendor prefix
 
 MTD driver changes:
 * st_spi_fsm: add missing clk_disable_unprepare() in stfsm_remove()
 * phram:
   - Allow cached mappings
   - Allow probing via reserved-memory
 * maps: ixp4xx: Drop driver
 * bcm47xxpart: Print correct offset on read error
 
 CFI driver changes:
 * Rename chip_ready variables
 * Add S29GL064N ID definition
 * Use chip_ready() for write on S29GL064N
 * Move and rename chip_check/chip_ready/chip_good_for_write
 
 NAND core changes:
 * Print offset instead of page number for bad blocks
 
 Raw NAND controller drivers:
 * Cadence: Fix possible null-ptr-deref in cadence_nand_dt_probe()
 * CS553X: simplify the return expression of cs553x_write_ctrl_byte()
 * Davinci: Remove redundant unsigned comparison to zero
 * Denali: Use managed device resources
 * GPMI:
   - Add large oob bch setting support
   - Rename the variable ecc_chunk_size
   - Uninline the gpmi_check_ecc function
   - Add strict ecc strength check
   - Refactor BCH geometry settings function
 * Intel: Fix possible null-ptr-deref in ebu_nand_probe()
 * MPC5121: Check before clk_disable_unprepare() not needed
 * Mtk:
   - MTD_NAND_ECC_MEDIATEK should depend on ARCH_MEDIATEK
   - Also parse the default nand-ecc-engine property if available
   - Make mtk_ecc.c a separated module
 * OMAP ELM:
   - Convert the bindings to yaml
   - Describe the bindings for AM64 ELM
   - Add support for its compatible
 * Renesas: Use runtime PM instead of the raw clock API and update the
            bindings accordingly
 * Rockchip: Check before clk_disable_unprepare() not needed
 * TMIO: Check return value after calling platform_get_resource()
 
 Raw NAND chip driver:
 * Kioxia: Add support for TH58NVG3S0HBAI4 and TC58NVG0S3HTA00
 
 SPI-NAND chip drivers:
 * Gigadevice:
   - Add support for:
     - GD5FxGM7xExxG
     - GD5F{2,4}GQ5xExxG
     - GD5F1GQ5RExxG
     - GD5FxGQ4xExxG
   - Fix Quad IO for GD5F1GQ5UExxG
 * XTX: Add support for XT26G0xA
 
 SPI NOR core changes:
 * Read back written SR value to make sure the write was done correctly.
 * Introduce a common function for Read ID that manufacturer drivers can
   use to verify the Octal DTR switch worked correctly.
 * Add helpers for read/write any register commands so manufacturer
   drivers don't open code it every time.
 * Clarify rdsr dummy cycles documentation.
 * Add debugfs entry to expose internal flash parameters and state.
 
 SPI NOR manufacturer drivers changes:
 * Add support for Winbond W25Q512NW-IM, and Eon EN25QH256A.
 * Move spi_nor_write_ear() to Winbond module since only Winbond flashes
   use it.
 * Rework Micron and Cypress Octal DTR enable methods to improve
   readability.
 * Use the common Read ID function to verify switch to Octal DTR mode for
   Micron and Cypress flashes.
 * Skip polling status on volatile register writes for Micron and Cypress
   flashes since the operation is instant.
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEE9HuaYnbmDhq/XIDIJWrqGEe9VoQFAmKHhXEACgkQJWrqGEe9
 VoQ4FQgAwTWnv7Eps8ngxlOE1WW1Kx+XUFCYN7mUKmDycgRKWWqF0OUu3pU6T3Si
 XoOixajzsPC7AJtXbbGKUkH3UtqapCZ/OVxMJzY1S1zKVgS72ChvPth+rNOA5H6f
 wkwCRYBCm51hYAlOQeRoFtwLEpOP17KTZw9Jn/u5RVwbQedQnzPj5sb6PddejjF4
 9nlk2auJHzENBEEBG3WLRNgvGep8mNNFarzWB4iWrMcG0w1EgBksbIdshoBmu7vj
 nKcDJbyA81DqX+8d56BfAFih8H2yCXZmhUzMK+zqnNkd1NuPnDDnrQYFKmKm2hAg
 3bIMIzi+8dl/m0TY6BHPHj8oIYpjYA==
 =P95J
 -----END PGP SIGNATURE-----

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

Pull mtd updates from Miquel Raynal:
 "MTD core changes:
   - Call of_platform_populate() for MTD partitions
   - Check devicetree alias for index
   - mtdoops:
      - Add a timestamp to the mtdoops header.
      - Create a header structure for the saved mtdoops.
      - Fix the size of the header read buffer.
   - mtdblock: Warn if opened on NAND
   - Bindings:
      - reserved-memory: Support MTD/block device
      - jedec,spi-nor: remove unneeded properties
      - Extend fixed-partitions binding
      - Add Sercomm (Suzhou) Corporation vendor prefix

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

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

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

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

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

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

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

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

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

View file

@ -1,16 +0,0 @@
Error location module
Required properties:
- compatible: Must be "ti,am3352-elm"
- reg: physical base address and size of the registers map.
- interrupts: Interrupt number for the elm.
Optional properties:
- ti,hwmods: Name of the hwmod associated to the elm
Example:
elm: elm@0 {
compatible = "ti,am3352-elm";
reg = <0x48080000 0x2000>;
interrupts = <4>;
};

View file

@ -50,10 +50,6 @@ properties:
minItems: 1
maxItems: 2
spi-max-frequency: true
spi-rx-bus-width: true
spi-tx-bus-width: true
m25p,fast-read:
type: boolean
description:
@ -74,8 +70,6 @@ properties:
be used on such systems, to denote the absence of a reliable reset
mechanism.
label: true
partitions:
type: object
@ -99,8 +93,6 @@ examples:
#size-cells = <0>;
flash@0 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "spansion,m25p80", "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <40000000>;

View file

@ -19,7 +19,11 @@ maintainers:
properties:
compatible:
const: fixed-partitions
oneOf:
- const: fixed-partitions
- items:
- const: sercomm,sc-partitions
- const: fixed-partitions
"#address-cells": true
@ -27,7 +31,24 @@ properties:
patternProperties:
"@[0-9a-f]+$":
$ref: "partition.yaml#"
allOf:
- $ref: "partition.yaml#"
- if:
properties:
compatible:
contains:
const: sercomm,sc-partitions
then:
properties:
sercomm,scpart-id:
description: Partition id in Sercomm partition map. Mtd
parser uses this id to find a record in the partition map
containing offset and size of the current partition. The
values from partition map overrides partition offset and
size defined in reg property of the dts. Frequently these
values are the same, but may differ if device has bad
eraseblocks on a flash.
$ref: /schemas/types.yaml#/definitions/uint32
required:
- "#address-cells"
@ -52,6 +73,7 @@ examples:
reg = <0x0100000 0x200000>;
};
};
- |
partitions {
compatible = "fixed-partitions";
@ -64,6 +86,7 @@ examples:
reg = <0x00000000 0x1 0x00000000>;
};
};
- |
partitions {
compatible = "fixed-partitions";
@ -82,6 +105,7 @@ examples:
reg = <0x2 0x00000000 0x1 0x00000000>;
};
};
- |
partitions {
compatible = "fixed-partitions";
@ -119,3 +143,30 @@ examples:
};
};
};
- |
partitions {
compatible = "sercomm,sc-partitions", "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "u-boot";
reg = <0x0 0x100000>;
sercomm,scpart-id = <0>;
read-only;
};
partition@100000 {
label = "dynamic partition map";
reg = <0x100000 0x100000>;
sercomm,scpart-id = <1>;
};
partition@200000 {
label = "Factory";
reg = <0x200000 0x100000>;
sercomm,scpart-id = <2>;
read-only;
};
};

View file

@ -36,11 +36,15 @@ properties:
- const: hclk
- const: eclk
power-domains:
maxItems: 1
required:
- compatible
- reg
- clocks
- clock-names
- power-domains
- interrupts
unevaluatedProperties: false
@ -56,6 +60,7 @@ examples:
interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&sysctrl R9A06G032_HCLK_NAND>, <&sysctrl R9A06G032_CLK_NAND>;
clock-names = "hclk", "eclk";
power-domains = <&sysctrl>;
#address-cells = <1>;
#size-cells = <0>;
};

View file

@ -0,0 +1,72 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mtd/ti,elm.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments Error Location Module (ELM).
maintainers:
- Roger Quadros <rogerq@kernel.org>
description:
ELM module is used together with GPMC and NAND Flash to detect
errors and the location of the error based on BCH algorithms
so they can be corrected if possible.
properties:
compatible:
enum:
- ti,am3352-elm
- ti,am64-elm
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
description: Functional clock.
clock-names:
items:
- const: fck
power-domains:
maxItems: 1
ti,hwmods:
description:
Name of the HWMOD associated with ELM. This is for legacy
platforms only.
$ref: /schemas/types.yaml#/definitions/string
deprecated: true
required:
- compatible
- reg
- interrupts
allOf:
- if:
properties:
compatible:
contains:
const: ti,am64-elm
then:
required:
- clocks
- clock-names
- power-domains
additionalProperties: false
examples:
- |
elm: ecc@0 {
compatible = "ti,am3352-elm";
reg = <0x0 0x2000>;
interrupts = <4>;
};

View file

@ -0,0 +1,47 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/reserved-memory/phram.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MTD/block device in RAM
description: |
Specifies that the reserved memory region can be used as an MTD or block
device.
The "phram" node is named after the "MTD in PHysical RAM" driver which
provides an implementation of this functionality in Linux.
maintainers:
- Vincent Whitchurch <vincent.whitchurch@axis.com>
allOf:
- $ref: "reserved-memory.yaml"
- $ref: "/schemas/mtd/mtd.yaml"
properties:
compatible:
const: phram
reg:
description: region of memory that can be used as an MTD/block device
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
phram: flash@12340000 {
compatible = "phram";
label = "rootfs";
reg = <0x12340000 0x00800000>;
};
};

View file

@ -1082,6 +1082,8 @@ patternProperties:
description: Sensirion AG
"^sensortek,.*":
description: Sensortek Technology Corporation
"^sercomm,.*":
description: Sercomm (Suzhou) Corporation
"^sff,.*":
description: Small Form Factor Committee
"^sgd,.*":

View file

@ -48,6 +48,7 @@
#define SST49LF040B 0x0050
#define SST49LF008A 0x005a
#define AT49BV6416 0x00d6
#define S29GL064N_MN12 0x0c01
/*
* Status Register bit description. Used by flash devices that don't
@ -59,6 +60,10 @@
#define CFI_SR_WBASB BIT(3)
#define CFI_SR_SLSB BIT(1)
enum cfi_quirks {
CFI_QUIRK_DQ_TRUE_DATA = BIT(0),
};
static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
#if !FORCE_WORD_WRITE
@ -436,6 +441,15 @@ static void fixup_s29ns512p_sectors(struct mtd_info *mtd)
mtd->name);
}
static void fixup_quirks(struct mtd_info *mtd)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
if (cfi->mfr == CFI_MFR_AMD && cfi->id == S29GL064N_MN12)
cfi->quirks |= CFI_QUIRK_DQ_TRUE_DATA;
}
/* Used to fix CFI-Tables of chips without Extended Query Tables */
static struct cfi_fixup cfi_nopri_fixup_table[] = {
{ CFI_MFR_SST, 0x234a, fixup_sst39vf }, /* SST39VF1602 */
@ -462,7 +476,7 @@ static struct cfi_fixup cfi_fixup_table[] = {
{ CFI_MFR_AMD, 0x0056, fixup_use_secsi },
{ CFI_MFR_AMD, 0x005C, fixup_use_secsi },
{ CFI_MFR_AMD, 0x005F, fixup_use_secsi },
{ CFI_MFR_AMD, 0x0c01, fixup_s29gl064n_sectors },
{ CFI_MFR_AMD, S29GL064N_MN12, fixup_s29gl064n_sectors },
{ CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors },
{ CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors },
{ CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors },
@ -474,6 +488,7 @@ static struct cfi_fixup cfi_fixup_table[] = {
#if !FORCE_WORD_WRITE
{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers },
#endif
{ CFI_MFR_ANY, CFI_ID_ANY, fixup_quirks },
{ 0, 0, NULL }
};
static struct cfi_fixup jedec_fixup_table[] = {
@ -801,47 +816,11 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
return NULL;
}
/*
* Return true if the chip is ready.
*
* Ready is one of: read mode, query mode, erase-suspend-read mode (in any
* non-suspended sector) and is indicated by no toggle bits toggling.
*
* Note that anything more complicated than checking if no bits are toggling
* (including checking DQ5 for an error status) is tricky to get working
* correctly and is therefore not done (particularly with interleaved chips
* as each chip must be checked independently of the others).
*/
static int __xipram chip_ready(struct map_info *map, struct flchip *chip,
unsigned long addr)
{
struct cfi_private *cfi = map->fldrv_priv;
map_word d, t;
if (cfi_use_status_reg(cfi)) {
map_word ready = CMD(CFI_SR_DRB);
/*
* For chips that support status register, check device
* ready bit
*/
cfi_send_gen_cmd(0x70, cfi->addr_unlock1, chip->start, map, cfi,
cfi->device_type, NULL);
d = map_read(map, addr);
return map_word_andequal(map, d, ready, ready);
}
d = map_read(map, addr);
t = map_read(map, addr);
return map_word_equal(map, d, t);
}
/*
* Return true if the chip is ready and has the correct value.
*
* Ready is one of: read mode, query mode, erase-suspend-read mode (in any
* non-suspended sector) and it is indicated by no bits toggling.
* non-suspended sector) and is indicated by no toggle bits toggling.
*
* Error are indicated by toggling bits or bits held with the wrong value,
* or with bits toggling.
@ -850,17 +829,16 @@ static int __xipram chip_ready(struct map_info *map, struct flchip *chip,
* (including checking DQ5 for an error status) is tricky to get working
* correctly and is therefore not done (particularly with interleaved chips
* as each chip must be checked independently of the others).
*
*/
static int __xipram chip_good(struct map_info *map, struct flchip *chip,
unsigned long addr, map_word expected)
static int __xipram chip_ready(struct map_info *map, struct flchip *chip,
unsigned long addr, map_word *expected)
{
struct cfi_private *cfi = map->fldrv_priv;
map_word oldd, curd;
int ret;
if (cfi_use_status_reg(cfi)) {
map_word ready = CMD(CFI_SR_DRB);
/*
* For chips that support status register, check device
* ready bit
@ -875,8 +853,24 @@ static int __xipram chip_good(struct map_info *map, struct flchip *chip,
oldd = map_read(map, addr);
curd = map_read(map, addr);
return map_word_equal(map, oldd, curd) &&
map_word_equal(map, curd, expected);
ret = map_word_equal(map, oldd, curd);
if (!ret || !expected)
return ret;
return map_word_equal(map, curd, *expected);
}
static int __xipram chip_good(struct map_info *map, struct flchip *chip,
unsigned long addr, map_word *expected)
{
struct cfi_private *cfi = map->fldrv_priv;
map_word *datum = expected;
if (cfi->quirks & CFI_QUIRK_DQ_TRUE_DATA)
datum = NULL;
return chip_ready(map, chip, addr, datum);
}
static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode)
@ -893,7 +887,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
case FL_STATUS:
for (;;) {
if (chip_ready(map, chip, adr))
if (chip_ready(map, chip, adr, NULL))
break;
if (time_after(jiffies, timeo)) {
@ -932,7 +926,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
chip->state = FL_ERASE_SUSPENDING;
chip->erase_suspended = 1;
for (;;) {
if (chip_ready(map, chip, adr))
if (chip_ready(map, chip, adr, NULL))
break;
if (time_after(jiffies, timeo)) {
@ -1463,7 +1457,7 @@ static int do_otp_lock(struct map_info *map, struct flchip *chip, loff_t adr,
/* wait for chip to become ready */
timeo = jiffies + msecs_to_jiffies(2);
for (;;) {
if (chip_ready(map, chip, adr))
if (chip_ready(map, chip, adr, NULL))
break;
if (time_after(jiffies, timeo)) {
@ -1699,7 +1693,7 @@ static int __xipram do_write_oneword_once(struct map_info *map,
* "chip_good" to avoid the failure due to scheduling.
*/
if (time_after(jiffies, timeo) &&
!chip_good(map, chip, adr, datum)) {
!chip_good(map, chip, adr, &datum)) {
xip_enable(map, chip, adr);
printk(KERN_WARNING "MTD %s(): software timeout\n", __func__);
xip_disable(map, chip, adr);
@ -1707,7 +1701,7 @@ static int __xipram do_write_oneword_once(struct map_info *map,
break;
}
if (chip_good(map, chip, adr, datum)) {
if (chip_good(map, chip, adr, &datum)) {
if (cfi_check_err_status(map, chip, adr))
ret = -EIO;
break;
@ -1979,14 +1973,14 @@ static int __xipram do_write_buffer_wait(struct map_info *map,
* "chip_good" to avoid the failure due to scheduling.
*/
if (time_after(jiffies, timeo) &&
!chip_good(map, chip, adr, datum)) {
!chip_good(map, chip, adr, &datum)) {
pr_err("MTD %s(): software timeout, address:0x%.8lx.\n",
__func__, adr);
ret = -EIO;
break;
}
if (chip_good(map, chip, adr, datum)) {
if (chip_good(map, chip, adr, &datum)) {
if (cfi_check_err_status(map, chip, adr))
ret = -EIO;
break;
@ -2195,7 +2189,7 @@ static int cfi_amdstd_panic_wait(struct map_info *map, struct flchip *chip,
* If the driver thinks the chip is idle, and no toggle bits
* are changing, then the chip is actually idle for sure.
*/
if (chip->state == FL_READY && chip_ready(map, chip, adr))
if (chip->state == FL_READY && chip_ready(map, chip, adr, NULL))
return 0;
/*
@ -2212,7 +2206,7 @@ static int cfi_amdstd_panic_wait(struct map_info *map, struct flchip *chip,
/* wait for the chip to become ready */
for (i = 0; i < jiffies_to_usecs(timeo); i++) {
if (chip_ready(map, chip, adr))
if (chip_ready(map, chip, adr, NULL))
return 0;
udelay(1);
@ -2276,13 +2270,13 @@ static int do_panic_write_oneword(struct map_info *map, struct flchip *chip,
map_write(map, datum, adr);
for (i = 0; i < jiffies_to_usecs(uWriteTimeout); i++) {
if (chip_ready(map, chip, adr))
if (chip_ready(map, chip, adr, NULL))
break;
udelay(1);
}
if (!chip_good(map, chip, adr, datum) ||
if (!chip_ready(map, chip, adr, &datum) ||
cfi_check_err_status(map, chip, adr)) {
/* reset on all failures. */
map_write(map, CMD(0xF0), chip->start);
@ -2424,6 +2418,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip)
DECLARE_WAITQUEUE(wait, current);
int ret;
int retry_cnt = 0;
map_word datum = map_word_ff(map);
adr = cfi->addr_unlock1;
@ -2478,7 +2473,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip)
chip->erase_suspended = 0;
}
if (chip_good(map, chip, adr, map_word_ff(map))) {
if (chip_ready(map, chip, adr, &datum)) {
if (cfi_check_err_status(map, chip, adr))
ret = -EIO;
break;
@ -2523,6 +2518,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
DECLARE_WAITQUEUE(wait, current);
int ret;
int retry_cnt = 0;
map_word datum = map_word_ff(map);
adr += chip->start;
@ -2577,7 +2573,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
chip->erase_suspended = 0;
}
if (chip_good(map, chip, adr, map_word_ff(map))) {
if (chip_ready(map, chip, adr, &datum)) {
if (cfi_check_err_status(map, chip, adr))
ret = -EIO;
break;
@ -2771,7 +2767,7 @@ static int __maybe_unused do_ppb_xxlock(struct map_info *map,
*/
timeo = jiffies + msecs_to_jiffies(2000); /* 2s max (un)locking */
for (;;) {
if (chip_ready(map, chip, adr))
if (chip_ready(map, chip, adr, NULL))
break;
if (time_after(jiffies, timeo)) {

View file

@ -27,10 +27,14 @@
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <asm/div64.h>
#include <linux/platform_device.h>
#include <linux/of_address.h>
#include <linux/of.h>
struct phram_mtd_list {
struct mtd_info mtd;
struct list_head list;
bool cached;
};
static LIST_HEAD(phram_list);
@ -77,20 +81,51 @@ static int phram_write(struct mtd_info *mtd, loff_t to, size_t len,
return 0;
}
static int phram_map(struct phram_mtd_list *phram, phys_addr_t start, size_t len)
{
void *addr = NULL;
if (phram->cached)
addr = memremap(start, len, MEMREMAP_WB);
else
addr = (void __force *)ioremap(start, len);
if (!addr)
return -EIO;
phram->mtd.priv = addr;
return 0;
}
static void phram_unmap(struct phram_mtd_list *phram)
{
void *addr = phram->mtd.priv;
if (phram->cached) {
memunmap(addr);
return;
}
iounmap((void __iomem *)addr);
}
static void unregister_devices(void)
{
struct phram_mtd_list *this, *safe;
list_for_each_entry_safe(this, safe, &phram_list, list) {
mtd_device_unregister(&this->mtd);
iounmap(this->mtd.priv);
phram_unmap(this);
kfree(this->mtd.name);
kfree(this);
}
}
static int register_device(char *name, phys_addr_t start, size_t len, uint32_t erasesize)
static int register_device(struct platform_device *pdev, const char *name,
phys_addr_t start, size_t len, uint32_t erasesize)
{
struct device_node *np = pdev ? pdev->dev.of_node : NULL;
bool cached = np ? !of_property_read_bool(np, "no-map") : false;
struct phram_mtd_list *new;
int ret = -ENOMEM;
@ -98,9 +133,10 @@ static int register_device(char *name, phys_addr_t start, size_t len, uint32_t e
if (!new)
goto out0;
ret = -EIO;
new->mtd.priv = ioremap(start, len);
if (!new->mtd.priv) {
new->cached = cached;
ret = phram_map(new, start, len);
if (ret) {
pr_err("ioremap failed\n");
goto out1;
}
@ -119,17 +155,23 @@ static int register_device(char *name, phys_addr_t start, size_t len, uint32_t e
new->mtd.erasesize = erasesize;
new->mtd.writesize = 1;
mtd_set_of_node(&new->mtd, np);
ret = -EAGAIN;
if (mtd_device_register(&new->mtd, NULL, 0)) {
pr_err("Failed to register new device\n");
goto out2;
}
list_add_tail(&new->list, &phram_list);
if (pdev)
platform_set_drvdata(pdev, new);
else
list_add_tail(&new->list, &phram_list);
return 0;
out2:
iounmap(new->mtd.priv);
phram_unmap(new);
out1:
kfree(new);
out0:
@ -278,7 +320,7 @@ static int phram_setup(const char *val)
goto error;
}
ret = register_device(name, start, len, (uint32_t)erasesize);
ret = register_device(NULL, name, start, len, (uint32_t)erasesize);
if (ret)
goto error;
@ -325,10 +367,54 @@ static int phram_param_call(const char *val, const struct kernel_param *kp)
module_param_call(phram, phram_param_call, NULL, NULL, 0200);
MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>[,<erasesize>]\"");
#ifdef CONFIG_OF
static const struct of_device_id phram_of_match[] = {
{ .compatible = "phram" },
{}
};
MODULE_DEVICE_TABLE(of, phram_of_match);
#endif
static int phram_probe(struct platform_device *pdev)
{
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENOMEM;
/* mtd_set_of_node() reads name from "label" */
return register_device(pdev, NULL, res->start, resource_size(res),
PAGE_SIZE);
}
static int phram_remove(struct platform_device *pdev)
{
struct phram_mtd_list *phram = platform_get_drvdata(pdev);
mtd_device_unregister(&phram->mtd);
phram_unmap(phram);
kfree(phram);
return 0;
}
static struct platform_driver phram_driver = {
.probe = phram_probe,
.remove = phram_remove,
.driver = {
.name = "phram",
.of_match_table = of_match_ptr(phram_of_match),
},
};
static int __init init_phram(void)
{
int ret = 0;
int ret;
ret = platform_driver_register(&phram_driver);
if (ret)
return ret;
#ifndef MODULE
if (phram_paramline[0])
@ -336,12 +422,16 @@ static int __init init_phram(void)
phram_init_called = 1;
#endif
if (ret)
platform_driver_unregister(&phram_driver);
return ret;
}
static void __exit cleanup_phram(void)
{
unregister_devices();
platform_driver_unregister(&phram_driver);
}
module_init(init_phram);

View file

@ -2126,6 +2126,8 @@ static int stfsm_remove(struct platform_device *pdev)
{
struct stfsm *fsm = platform_get_drvdata(pdev);
clk_disable_unprepare(fsm->clk);
return mtd_device_unregister(&fsm->mtd);
}

View file

@ -300,15 +300,6 @@ config MTD_DC21285
21285 bridge used with Intel's StrongARM processors. More info at
<https://www.intel.com/design/bridge/docs/21285_documentation.htm>.
config MTD_IXP4XX
tristate "CFI Flash device mapped on Intel IXP4xx based systems"
depends on MTD_CFI && MTD_COMPLEX_MAPPINGS && ARCH_IXP4XX && MTD_CFI_ADV_OPTIONS
help
This enables MTD access to flash devices on platforms based
on Intel's IXP4xx family of network processors such as the
IXDP425 and Coyote. If you have an IXP4xx based board and
would like to use the flash chips on it, say 'Y'.
config MTD_IMPA7
tristate "JEDEC Flash device mapped on impA7"
depends on ARM && MTD_JEDECPROBE

View file

@ -39,7 +39,6 @@ obj-$(CONFIG_MTD_IMPA7) += impa7.o
obj-$(CONFIG_MTD_UCLINUX) += uclinux.o
obj-$(CONFIG_MTD_NETtel) += nettel.o
obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o
obj-$(CONFIG_MTD_IXP4XX) += ixp4xx.o
obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o
obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o
obj-$(CONFIG_MTD_VMU) += vmu-flash.o

View file

@ -1,262 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* drivers/mtd/maps/ixp4xx.c
*
* MTD Map file for IXP4XX based systems. Please do not make per-board
* changes in here. If your board needs special setup, do it in your
* platform level code in arch/arm/mach-ixp4xx/board-setup.c
*
* Original Author: Intel Corporation
* Maintainer: Deepak Saxena <dsaxena@mvista.com>
*
* Copyright (C) 2002 Intel Corporation
* Copyright (C) 2003-2004 MontaVista Software, Inc.
*
*/
#include <linux/err.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <asm/mach/flash.h>
#include <linux/reboot.h>
/*
* Read/write a 16 bit word from flash address 'addr'.
*
* When the cpu is in little-endian mode it swizzles the address lines
* ('address coherency') so we need to undo the swizzling to ensure commands
* and the like end up on the correct flash address.
*
* To further complicate matters, due to the way the expansion bus controller
* handles 32 bit reads, the byte stream ABCD is stored on the flash as:
* D15 D0
* +---+---+
* | A | B | 0
* +---+---+
* | C | D | 2
* +---+---+
* This means that on LE systems each 16 bit word must be swapped. Note that
* this requires CONFIG_MTD_CFI_BE_BYTE_SWAP to be enabled to 'unswap' the CFI
* data and other flash commands which are always in D7-D0.
*/
#ifndef __ARMEB__
#ifndef CONFIG_MTD_CFI_BE_BYTE_SWAP
# error CONFIG_MTD_CFI_BE_BYTE_SWAP required
#endif
static inline u16 flash_read16(void __iomem *addr)
{
return be16_to_cpu(__raw_readw((void __iomem *)((unsigned long)addr ^ 0x2)));
}
static inline void flash_write16(u16 d, void __iomem *addr)
{
__raw_writew(cpu_to_be16(d), (void __iomem *)((unsigned long)addr ^ 0x2));
}
#define BYTE0(h) ((h) & 0xFF)
#define BYTE1(h) (((h) >> 8) & 0xFF)
#else
static inline u16 flash_read16(const void __iomem *addr)
{
return __raw_readw(addr);
}
static inline void flash_write16(u16 d, void __iomem *addr)
{
__raw_writew(d, addr);
}
#define BYTE0(h) (((h) >> 8) & 0xFF)
#define BYTE1(h) ((h) & 0xFF)
#endif
static map_word ixp4xx_read16(struct map_info *map, unsigned long ofs)
{
map_word val;
val.x[0] = flash_read16(map->virt + ofs);
return val;
}
/*
* The IXP4xx expansion bus only allows 16-bit wide acceses
* when attached to a 16-bit wide device (such as the 28F128J3A),
* so we can't just memcpy_fromio().
*/
static void ixp4xx_copy_from(struct map_info *map, void *to,
unsigned long from, ssize_t len)
{
u8 *dest = (u8 *) to;
void __iomem *src = map->virt + from;
if (len <= 0)
return;
if (from & 1) {
*dest++ = BYTE1(flash_read16(src-1));
src++;
--len;
}
while (len >= 2) {
u16 data = flash_read16(src);
*dest++ = BYTE0(data);
*dest++ = BYTE1(data);
src += 2;
len -= 2;
}
if (len > 0)
*dest++ = BYTE0(flash_read16(src));
}
/*
* Unaligned writes are ignored, causing the 8-bit
* probe to fail and proceed to the 16-bit probe (which succeeds).
*/
static void ixp4xx_probe_write16(struct map_info *map, map_word d, unsigned long adr)
{
if (!(adr & 1))
flash_write16(d.x[0], map->virt + adr);
}
/*
* Fast write16 function without the probing check above
*/
static void ixp4xx_write16(struct map_info *map, map_word d, unsigned long adr)
{
flash_write16(d.x[0], map->virt + adr);
}
struct ixp4xx_flash_info {
struct mtd_info *mtd;
struct map_info map;
struct resource *res;
};
static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL };
static int ixp4xx_flash_remove(struct platform_device *dev)
{
struct flash_platform_data *plat = dev_get_platdata(&dev->dev);
struct ixp4xx_flash_info *info = platform_get_drvdata(dev);
if(!info)
return 0;
if (info->mtd) {
mtd_device_unregister(info->mtd);
map_destroy(info->mtd);
}
if (plat->exit)
plat->exit();
return 0;
}
static int ixp4xx_flash_probe(struct platform_device *dev)
{
struct flash_platform_data *plat = dev_get_platdata(&dev->dev);
struct ixp4xx_flash_info *info;
struct mtd_part_parser_data ppdata = {
.origin = dev->resource->start,
};
int err = -1;
if (!plat)
return -ENODEV;
if (plat->init) {
err = plat->init();
if (err)
return err;
}
info = devm_kzalloc(&dev->dev, sizeof(struct ixp4xx_flash_info),
GFP_KERNEL);
if(!info) {
err = -ENOMEM;
goto Error;
}
platform_set_drvdata(dev, info);
/*
* Tell the MTD layer we're not 1:1 mapped so that it does
* not attempt to do a direct access on us.
*/
info->map.phys = NO_XIP;
info->map.size = resource_size(dev->resource);
/*
* We only support 16-bit accesses for now. If and when
* any board use 8-bit access, we'll fixup the driver to
* handle that.
*/
info->map.bankwidth = 2;
info->map.name = dev_name(&dev->dev);
info->map.read = ixp4xx_read16;
info->map.write = ixp4xx_probe_write16;
info->map.copy_from = ixp4xx_copy_from;
info->map.virt = devm_ioremap_resource(&dev->dev, dev->resource);
if (IS_ERR(info->map.virt)) {
err = PTR_ERR(info->map.virt);
goto Error;
}
info->mtd = do_map_probe(plat->map_name, &info->map);
if (!info->mtd) {
printk(KERN_ERR "IXP4XXFlash: map_probe failed\n");
err = -ENXIO;
goto Error;
}
info->mtd->dev.parent = &dev->dev;
/* Use the fast version */
info->map.write = ixp4xx_write16;
err = mtd_device_parse_register(info->mtd, probes, &ppdata,
plat->parts, plat->nr_parts);
if (err) {
printk(KERN_ERR "Could not parse partitions\n");
goto Error;
}
return 0;
Error:
ixp4xx_flash_remove(dev);
return err;
}
static struct platform_driver ixp4xx_flash_driver = {
.probe = ixp4xx_flash_probe,
.remove = ixp4xx_flash_remove,
.driver = {
.name = "IXP4XX-Flash",
},
};
module_platform_driver(ixp4xx_flash_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MTD map driver for Intel IXP4xx systems");
MODULE_AUTHOR("Deepak Saxena");
MODULE_ALIAS("platform:IXP4XX-Flash");

View file

@ -257,6 +257,10 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd)
return 0;
}
if (mtd_type_is_nand(mbd->mtd))
pr_warn("%s: MTD device '%s' is NAND, please consider using UBI block devices instead.\n",
mbd->tr->name, mbd->mtd->name);
/* OK, it's not open. Create cache info for it */
mtdblk->count = 1;
mutex_init(&mtdblk->cache_mutex);
@ -322,10 +326,6 @@ static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
if (!(mtd->flags & MTD_WRITEABLE))
dev->mbd.readonly = 1;
if (mtd_type_is_nand(mtd))
pr_warn("%s: MTD device '%s' is NAND, please consider using UBI block devices instead.\n",
tr->name, mtd->name);
if (add_mtd_blktrans_dev(&dev->mbd))
kfree(dev);
}

View file

@ -557,9 +557,10 @@ static int mtd_nvmem_add(struct mtd_info *mtd)
int add_mtd_device(struct mtd_info *mtd)
{
struct device_node *np = mtd_get_of_node(mtd);
struct mtd_info *master = mtd_get_master(mtd);
struct mtd_notifier *not;
int i, error;
int i, error, ofidx;
/*
* May occur, for instance, on buggy drivers which call
@ -598,7 +599,13 @@ int add_mtd_device(struct mtd_info *mtd)
mutex_lock(&mtd_table_mutex);
i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL);
ofidx = -1;
if (np)
ofidx = of_alias_get_id(np, "mtd");
if (ofidx >= 0)
i = idr_alloc(&mtd_idr, mtd, ofidx, ofidx + 1, GFP_KERNEL);
else
i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL);
if (i < 0) {
error = i;
goto fail_locked;

View file

@ -16,15 +16,13 @@
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/timekeeping.h>
#include <linux/mtd/mtd.h>
#include <linux/kmsg_dump.h>
/* Maximum MTD partition size */
#define MTDOOPS_MAX_MTD_SIZE (8 * 1024 * 1024)
#define MTDOOPS_KERNMSG_MAGIC 0x5d005d00
#define MTDOOPS_HEADER_SIZE 8
static unsigned long record_size = 4096;
module_param(record_size, ulong, 0400);
MODULE_PARM_DESC(record_size,
@ -40,6 +38,15 @@ module_param(dump_oops, int, 0600);
MODULE_PARM_DESC(dump_oops,
"set to 1 to dump oopses, 0 to only dump panics (default 1)");
#define MTDOOPS_KERNMSG_MAGIC_v1 0x5d005d00 /* Original */
#define MTDOOPS_KERNMSG_MAGIC_v2 0x5d005e00 /* Adds the timestamp */
struct mtdoops_hdr {
u32 seq;
u32 magic;
ktime_t timestamp;
} __packed;
static struct mtdoops_context {
struct kmsg_dumper dump;
@ -178,16 +185,17 @@ static void mtdoops_write(struct mtdoops_context *cxt, int panic)
{
struct mtd_info *mtd = cxt->mtd;
size_t retlen;
u32 *hdr;
struct mtdoops_hdr *hdr;
int ret;
if (test_and_set_bit(0, &cxt->oops_buf_busy))
return;
/* Add mtdoops header to the buffer */
hdr = cxt->oops_buf;
hdr[0] = cxt->nextcount;
hdr[1] = MTDOOPS_KERNMSG_MAGIC;
hdr = (struct mtdoops_hdr *)cxt->oops_buf;
hdr->seq = cxt->nextcount;
hdr->magic = MTDOOPS_KERNMSG_MAGIC_v2;
hdr->timestamp = ktime_get_real();
if (panic) {
ret = mtd_panic_write(mtd, cxt->nextpage * record_size,
@ -222,8 +230,9 @@ static void mtdoops_workfunc_write(struct work_struct *work)
static void find_next_position(struct mtdoops_context *cxt)
{
struct mtd_info *mtd = cxt->mtd;
struct mtdoops_hdr hdr;
int ret, page, maxpos = 0;
u32 count[2], maxcount = 0xffffffff;
u32 maxcount = 0xffffffff;
size_t retlen;
for (page = 0; page < cxt->oops_pages; page++) {
@ -231,32 +240,33 @@ static void find_next_position(struct mtdoops_context *cxt)
continue;
/* Assume the page is used */
mark_page_used(cxt, page);
ret = mtd_read(mtd, page * record_size, MTDOOPS_HEADER_SIZE,
&retlen, (u_char *)&count[0]);
if (retlen != MTDOOPS_HEADER_SIZE ||
ret = mtd_read(mtd, page * record_size, sizeof(hdr),
&retlen, (u_char *)&hdr);
if (retlen != sizeof(hdr) ||
(ret < 0 && !mtd_is_bitflip(ret))) {
printk(KERN_ERR "mtdoops: read failure at %ld (%td of %d read), err %d\n",
page * record_size, retlen,
MTDOOPS_HEADER_SIZE, ret);
printk(KERN_ERR "mtdoops: read failure at %ld (%zu of %zu read), err %d\n",
page * record_size, retlen, sizeof(hdr), ret);
continue;
}
if (count[0] == 0xffffffff && count[1] == 0xffffffff)
if (hdr.seq == 0xffffffff && hdr.magic == 0xffffffff)
mark_page_unused(cxt, page);
if (count[0] == 0xffffffff || count[1] != MTDOOPS_KERNMSG_MAGIC)
if (hdr.seq == 0xffffffff ||
(hdr.magic != MTDOOPS_KERNMSG_MAGIC_v1 &&
hdr.magic != MTDOOPS_KERNMSG_MAGIC_v2))
continue;
if (maxcount == 0xffffffff) {
maxcount = count[0];
maxcount = hdr.seq;
maxpos = page;
} else if (count[0] < 0x40000000 && maxcount > 0xc0000000) {
maxcount = count[0];
} else if (hdr.seq < 0x40000000 && maxcount > 0xc0000000) {
maxcount = hdr.seq;
maxpos = page;
} else if (count[0] > maxcount && count[0] < 0xc0000000) {
maxcount = count[0];
} else if (hdr.seq > maxcount && hdr.seq < 0xc0000000) {
maxcount = hdr.seq;
maxpos = page;
} else if (count[0] > maxcount && count[0] > 0xc0000000
} else if (hdr.seq > maxcount && hdr.seq > 0xc0000000
&& maxcount > 0x80000000) {
maxcount = count[0];
maxcount = hdr.seq;
maxpos = page;
}
}
@ -287,8 +297,9 @@ static void mtdoops_do_dump(struct kmsg_dumper *dumper,
if (test_and_set_bit(0, &cxt->oops_buf_busy))
return;
kmsg_dump_get_buffer(&iter, true, cxt->oops_buf + MTDOOPS_HEADER_SIZE,
record_size - MTDOOPS_HEADER_SIZE, NULL);
kmsg_dump_get_buffer(&iter, true,
cxt->oops_buf + sizeof(struct mtdoops_hdr),
record_size - sizeof(struct mtdoops_hdr), NULL);
clear_bit(0, &cxt->oops_buf_busy);
if (reason != KMSG_DUMP_OOPS) {

View file

@ -17,6 +17,7 @@
#include <linux/mtd/partitions.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include "mtdcore.h"
@ -577,10 +578,16 @@ static int mtd_part_of_parse(struct mtd_info *master,
struct mtd_part_parser *parser;
struct device_node *np;
struct property *prop;
struct device *dev;
const char *compat;
const char *fixed = "fixed-partitions";
int ret, err = 0;
dev = &master->dev;
/* Use parent device (controller) if the top level MTD is not registered */
if (!IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) && !mtd_is_partition(master))
dev = master->dev.parent;
np = mtd_get_of_node(master);
if (mtd_is_partition(master))
of_node_get(np);
@ -593,6 +600,7 @@ static int mtd_part_of_parse(struct mtd_info *master,
continue;
ret = mtd_part_do_parse(parser, master, pparts, NULL);
if (ret > 0) {
of_platform_populate(np, NULL, NULL, dev);
of_node_put(np);
return ret;
}
@ -600,6 +608,7 @@ static int mtd_part_of_parse(struct mtd_info *master,
if (ret < 0 && !err)
err = ret;
}
of_platform_populate(np, NULL, NULL, dev);
of_node_put(np);
/*

View file

@ -53,6 +53,14 @@ config MTD_NAND_ECC_MXIC
help
This enables support for the hardware ECC engine from Macronix.
config MTD_NAND_ECC_MEDIATEK
tristate "Mediatek hardware ECC engine"
depends on HAS_IOMEM
depends on ARCH_MEDIATEK || COMPILE_TEST
select MTD_NAND_ECC
help
This enables support for the hardware ECC engine from Mediatek.
endmenu
endmenu

View file

@ -2,6 +2,7 @@
nandcore-objs := core.o bbt.o
obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o
obj-$(CONFIG_MTD_NAND_ECC_MEDIATEK) += ecc-mtk.o
obj-y += onenand/
obj-y += raw/

View file

@ -15,8 +15,7 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/mutex.h>
#include "mtk_ecc.h"
#include <linux/mtd/nand-ecc-mtk.h>
#define ECC_IDLE_MASK BIT(0)
#define ECC_IRQ_EN BIT(0)
@ -280,7 +279,10 @@ struct mtk_ecc *of_mtk_ecc_get(struct device_node *of_node)
struct mtk_ecc *ecc = NULL;
struct device_node *np;
np = of_parse_phandle(of_node, "ecc-engine", 0);
np = of_parse_phandle(of_node, "nand-ecc-engine", 0);
/* for backward compatibility */
if (!np)
np = of_parse_phandle(of_node, "ecc-engine", 0);
if (np) {
ecc = mtk_ecc_get(np);
of_node_put(np);

View file

@ -374,6 +374,7 @@ config MTD_NAND_QCOM
config MTD_NAND_MTK
tristate "MTK NAND controller"
depends on MTD_NAND_ECC_MEDIATEK
depends on ARCH_MEDIATEK || COMPILE_TEST
depends on HAS_IOMEM
help

View file

@ -48,7 +48,7 @@ obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o
obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o
obj-$(CONFIG_MTD_NAND_MTK) += mtk_nand.o
obj-$(CONFIG_MTD_NAND_MXIC) += mxic_nand.o
obj-$(CONFIG_MTD_NAND_TEGRA) += tegra_nand.o
obj-$(CONFIG_MTD_NAND_STM32_FMC2) += stm32_fmc2_nand.o

View file

@ -2983,11 +2983,10 @@ static int cadence_nand_dt_probe(struct platform_device *ofdev)
if (IS_ERR(cdns_ctrl->reg))
return PTR_ERR(cdns_ctrl->reg);
res = platform_get_resource(ofdev, IORESOURCE_MEM, 1);
cdns_ctrl->io.dma = res->start;
cdns_ctrl->io.virt = devm_ioremap_resource(&ofdev->dev, res);
cdns_ctrl->io.virt = devm_platform_get_and_ioremap_resource(ofdev, 1, &res);
if (IS_ERR(cdns_ctrl->io.virt))
return PTR_ERR(cdns_ctrl->io.virt);
cdns_ctrl->io.dma = res->start;
dt->clk = devm_clk_get(cdns_ctrl->dev, "nf_clk");
if (IS_ERR(dt->clk))

View file

@ -104,17 +104,12 @@ static int cs553x_write_ctrl_byte(struct cs553x_nand_controller *cs553x,
u32 ctl, u8 data)
{
u8 status;
int ret;
writeb(ctl, cs553x->mmio + MM_NAND_CTL);
writeb(data, cs553x->mmio + MM_NAND_IO);
ret = readb_poll_timeout_atomic(cs553x->mmio + MM_NAND_STS, status,
return readb_poll_timeout_atomic(cs553x->mmio + MM_NAND_STS, status,
!(status & CS_NAND_CTLR_BUSY), 1,
100000);
if (ret)
return ret;
return 0;
}
static void cs553x_data_in(struct cs553x_nand_controller *cs553x, void *buf,

View file

@ -727,7 +727,7 @@ static int nand_davinci_probe(struct platform_device *pdev)
return -ENODEV;
/* which external chipselect will we be managing? */
if (pdata->core_chipsel < 0 || pdata->core_chipsel > 3)
if (pdata->core_chipsel > 3)
return -ENODEV;
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);

View file

@ -74,22 +74,21 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
return ret;
}
denali->reg = ioremap(csr_base, csr_len);
denali->reg = devm_ioremap(denali->dev, csr_base, csr_len);
if (!denali->reg) {
dev_err(&dev->dev, "Spectra: Unable to remap memory region\n");
return -ENOMEM;
}
denali->host = ioremap(mem_base, mem_len);
denali->host = devm_ioremap(denali->dev, mem_base, mem_len);
if (!denali->host) {
dev_err(&dev->dev, "Spectra: ioremap failed!");
ret = -ENOMEM;
goto out_unmap_reg;
return -ENOMEM;
}
ret = denali_init(denali);
if (ret)
goto out_unmap_host;
return ret;
nsels = denali->nbanks;
@ -117,10 +116,6 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
out_remove_denali:
denali_remove(denali);
out_unmap_host:
iounmap(denali->host);
out_unmap_reg:
iounmap(denali->reg);
return ret;
}
@ -129,8 +124,6 @@ static void denali_pci_remove(struct pci_dev *dev)
struct denali_controller *denali = pci_get_drvdata(dev);
denali_remove(denali);
iounmap(denali->reg);
iounmap(denali->host);
}
static struct pci_driver denali_pci_driver = {

View file

@ -218,7 +218,8 @@ static void gpmi_dump_info(struct gpmi_nand_data *this)
"ECC Strength : %u\n"
"Page Size in Bytes : %u\n"
"Metadata Size in Bytes : %u\n"
"ECC Chunk Size in Bytes: %u\n"
"ECC0 Chunk Size in Bytes: %u\n"
"ECCn Chunk Size in Bytes: %u\n"
"ECC Chunk Count : %u\n"
"Payload Size in Bytes : %u\n"
"Auxiliary Size in Bytes: %u\n"
@ -229,7 +230,8 @@ static void gpmi_dump_info(struct gpmi_nand_data *this)
geo->ecc_strength,
geo->page_size,
geo->metadata_size,
geo->ecc_chunk_size,
geo->ecc0_chunk_size,
geo->eccn_chunk_size,
geo->ecc_chunk_count,
geo->payload_size,
geo->auxiliary_size,
@ -238,9 +240,15 @@ static void gpmi_dump_info(struct gpmi_nand_data *this)
geo->block_mark_bit_offset);
}
static inline bool gpmi_check_ecc(struct gpmi_nand_data *this)
static bool gpmi_check_ecc(struct gpmi_nand_data *this)
{
struct nand_chip *chip = &this->nand;
struct bch_geometry *geo = &this->bch_geometry;
struct nand_device *nand = &chip->base;
struct nand_ecc_props *conf = &nand->ecc.ctx.conf;
conf->step_size = geo->eccn_chunk_size;
conf->strength = geo->ecc_strength;
/* Do the sanity check. */
if (GPMI_IS_MXS(this)) {
@ -248,7 +256,47 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this)
if (geo->gf_len == 14)
return false;
}
return geo->ecc_strength <= this->devdata->bch_max_ecc_strength;
if (geo->ecc_strength > this->devdata->bch_max_ecc_strength)
return false;
if (!nand_ecc_is_strong_enough(nand))
return false;
return true;
}
/* check if bbm locates in data chunk rather than ecc chunk */
static bool bbm_in_data_chunk(struct gpmi_nand_data *this,
unsigned int *chunk_num)
{
struct bch_geometry *geo = &this->bch_geometry;
struct nand_chip *chip = &this->nand;
struct mtd_info *mtd = nand_to_mtd(chip);
unsigned int i, j;
if (geo->ecc0_chunk_size != geo->eccn_chunk_size) {
dev_err(this->dev,
"The size of ecc0_chunk must equal to eccn_chunk\n");
return false;
}
i = (mtd->writesize * 8 - geo->metadata_size * 8) /
(geo->gf_len * geo->ecc_strength +
geo->eccn_chunk_size * 8);
j = (mtd->writesize * 8 - geo->metadata_size * 8) -
(geo->gf_len * geo->ecc_strength +
geo->eccn_chunk_size * 8) * i;
if (j < geo->eccn_chunk_size * 8) {
*chunk_num = i+1;
dev_dbg(this->dev, "Set ecc to %d and bbm in chunk %d\n",
geo->ecc_strength, *chunk_num);
return true;
}
return false;
}
/*
@ -280,13 +328,14 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this,
nanddev_get_ecc_requirements(&chip->base)->step_size);
return -EINVAL;
}
geo->ecc_chunk_size = ecc_step;
geo->ecc0_chunk_size = ecc_step;
geo->eccn_chunk_size = ecc_step;
geo->ecc_strength = round_up(ecc_strength, 2);
if (!gpmi_check_ecc(this))
return -EINVAL;
/* Keep the C >= O */
if (geo->ecc_chunk_size < mtd->oobsize) {
if (geo->eccn_chunk_size < mtd->oobsize) {
dev_err(this->dev,
"unsupported nand chip. ecc size: %d, oob size : %d\n",
ecc_step, mtd->oobsize);
@ -296,7 +345,7 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this,
/* The default value, see comment in the legacy_set_geometry(). */
geo->metadata_size = 10;
geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size;
geo->ecc_chunk_count = mtd->writesize / geo->eccn_chunk_size;
/*
* Now, the NAND chip with 2K page(data chunk is 512byte) shows below:
@ -399,6 +448,134 @@ static inline int get_ecc_strength(struct gpmi_nand_data *this)
return round_down(ecc_strength, 2);
}
static int set_geometry_for_large_oob(struct gpmi_nand_data *this)
{
struct bch_geometry *geo = &this->bch_geometry;
struct nand_chip *chip = &this->nand;
struct mtd_info *mtd = nand_to_mtd(chip);
const struct nand_ecc_props *requirements =
nanddev_get_ecc_requirements(&chip->base);
unsigned int block_mark_bit_offset;
unsigned int max_ecc;
unsigned int bbm_chunk;
unsigned int i;
/* sanity check for the minimum ecc nand required */
if (!(requirements->strength > 0 &&
requirements->step_size > 0))
return -EINVAL;
geo->ecc_strength = requirements->strength;
/* check if platform can support this nand */
if (!gpmi_check_ecc(this)) {
dev_err(this->dev,
"unsupported NAND chip, minimum ecc required %d\n",
geo->ecc_strength);
return -EINVAL;
}
/* calculate the maximum ecc platform can support*/
geo->metadata_size = 10;
geo->gf_len = 14;
geo->ecc0_chunk_size = 1024;
geo->eccn_chunk_size = 1024;
geo->ecc_chunk_count = mtd->writesize / geo->eccn_chunk_size;
max_ecc = min(get_ecc_strength(this),
this->devdata->bch_max_ecc_strength);
/*
* search a supported ecc strength that makes bbm
* located in data chunk
*/
geo->ecc_strength = max_ecc;
while (!(geo->ecc_strength < requirements->strength)) {
if (bbm_in_data_chunk(this, &bbm_chunk))
goto geo_setting;
geo->ecc_strength -= 2;
}
/* if none of them works, keep using the minimum ecc */
/* nand required but changing ecc page layout */
geo->ecc_strength = requirements->strength;
/* add extra ecc for meta data */
geo->ecc0_chunk_size = 0;
geo->ecc_chunk_count = (mtd->writesize / geo->eccn_chunk_size) + 1;
geo->ecc_for_meta = 1;
/* check if oob can afford this extra ecc chunk */
if (mtd->oobsize * 8 < geo->metadata_size * 8 +
geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) {
dev_err(this->dev, "unsupported NAND chip with new layout\n");
return -EINVAL;
}
/* calculate in which chunk bbm located */
bbm_chunk = (mtd->writesize * 8 - geo->metadata_size * 8 -
geo->gf_len * geo->ecc_strength) /
(geo->gf_len * geo->ecc_strength +
geo->eccn_chunk_size * 8) + 1;
geo_setting:
geo->page_size = mtd->writesize + geo->metadata_size +
(geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8;
geo->payload_size = mtd->writesize;
/*
* The auxiliary buffer contains the metadata and the ECC status. The
* metadata is padded to the nearest 32-bit boundary. The ECC status
* contains one byte for every ECC chunk, and is also padded to the
* nearest 32-bit boundary.
*/
geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4);
geo->auxiliary_size = ALIGN(geo->metadata_size, 4)
+ ALIGN(geo->ecc_chunk_count, 4);
if (!this->swap_block_mark)
return 0;
/* calculate the number of ecc chunk behind the bbm */
i = (mtd->writesize / geo->eccn_chunk_size) - bbm_chunk + 1;
block_mark_bit_offset = mtd->writesize * 8 -
(geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - i)
+ geo->metadata_size * 8);
geo->block_mark_byte_offset = block_mark_bit_offset / 8;
geo->block_mark_bit_offset = block_mark_bit_offset % 8;
dev_dbg(this->dev, "BCH Geometry :\n"
"GF length : %u\n"
"ECC Strength : %u\n"
"Page Size in Bytes : %u\n"
"Metadata Size in Bytes : %u\n"
"ECC0 Chunk Size in Bytes: %u\n"
"ECCn Chunk Size in Bytes: %u\n"
"ECC Chunk Count : %u\n"
"Payload Size in Bytes : %u\n"
"Auxiliary Size in Bytes: %u\n"
"Auxiliary Status Offset: %u\n"
"Block Mark Byte Offset : %u\n"
"Block Mark Bit Offset : %u\n"
"Block Mark in chunk : %u\n"
"Ecc for Meta data : %u\n",
geo->gf_len,
geo->ecc_strength,
geo->page_size,
geo->metadata_size,
geo->ecc0_chunk_size,
geo->eccn_chunk_size,
geo->ecc_chunk_count,
geo->payload_size,
geo->auxiliary_size,
geo->auxiliary_status_offset,
geo->block_mark_byte_offset,
geo->block_mark_bit_offset,
bbm_chunk,
geo->ecc_for_meta);
return 0;
}
static int legacy_set_geometry(struct gpmi_nand_data *this)
{
struct bch_geometry *geo = &this->bch_geometry;
@ -418,13 +595,15 @@ static int legacy_set_geometry(struct gpmi_nand_data *this)
geo->gf_len = 13;
/* The default for chunk size. */
geo->ecc_chunk_size = 512;
while (geo->ecc_chunk_size < mtd->oobsize) {
geo->ecc_chunk_size *= 2; /* keep C >= O */
geo->ecc0_chunk_size = 512;
geo->eccn_chunk_size = 512;
while (geo->eccn_chunk_size < mtd->oobsize) {
geo->ecc0_chunk_size *= 2; /* keep C >= O */
geo->eccn_chunk_size *= 2; /* keep C >= O */
geo->gf_len = 14;
}
geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size;
geo->ecc_chunk_count = mtd->writesize / geo->eccn_chunk_size;
/* We use the same ECC strength for all chunks. */
geo->ecc_strength = get_ecc_strength(this);
@ -514,24 +693,40 @@ static int legacy_set_geometry(struct gpmi_nand_data *this)
static int common_nfc_set_geometry(struct gpmi_nand_data *this)
{
struct nand_chip *chip = &this->nand;
struct mtd_info *mtd = nand_to_mtd(&this->nand);
const struct nand_ecc_props *requirements =
nanddev_get_ecc_requirements(&chip->base);
bool use_minimun_ecc;
int err;
if (chip->ecc.strength > 0 && chip->ecc.size > 0)
return set_geometry_by_ecc_info(this, chip->ecc.strength,
chip->ecc.size);
use_minimun_ecc = of_property_read_bool(this->dev->of_node,
"fsl,use-minimum-ecc");
if ((of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc"))
|| legacy_set_geometry(this)) {
if (!(requirements->strength > 0 && requirements->step_size > 0))
return -EINVAL;
return set_geometry_by_ecc_info(this,
requirements->strength,
requirements->step_size);
/* use legacy bch geometry settings by default*/
if ((!use_minimun_ecc && mtd->oobsize < 1024) ||
!(requirements->strength > 0 && requirements->step_size > 0)) {
dev_dbg(this->dev, "use legacy bch geometry\n");
err = legacy_set_geometry(this);
if (!err)
return 0;
}
return 0;
/* for large oob nand */
if (mtd->oobsize > 1024) {
dev_dbg(this->dev, "use large oob bch geometry\n");
err = set_geometry_for_large_oob(this);
if (!err)
return 0;
}
/* otherwise use the minimum ecc nand chip required */
dev_dbg(this->dev, "use minimum ecc bch geometry\n");
err = set_geometry_by_ecc_info(this, requirements->strength,
requirements->step_size);
if (err)
dev_err(this->dev, "none of the bch geometry setting works\n");
return err;
}
/* Configures the geometry for BCH. */
@ -843,7 +1038,7 @@ static int gpmi_raw_len_to_len(struct gpmi_nand_data *this, int raw_len)
* we are passed in exec_op. Calculate the data length from it.
*/
if (this->bch)
return ALIGN_DOWN(raw_len, this->bch_geometry.ecc_chunk_size);
return ALIGN_DOWN(raw_len, this->bch_geometry.eccn_chunk_size);
else
return raw_len;
}
@ -1235,7 +1430,7 @@ static int gpmi_count_bitflips(struct nand_chip *chip, void *buf, int first,
/* Read ECC bytes into our internal raw_buffer */
offset = nfc_geo->metadata_size * 8;
offset += ((8 * nfc_geo->ecc_chunk_size) + eccbits) * (i + 1);
offset += ((8 * nfc_geo->eccn_chunk_size) + eccbits) * (i + 1);
offset -= eccbits;
bitoffset = offset % 8;
eccbytes = DIV_ROUND_UP(offset + eccbits, 8);
@ -1272,16 +1467,16 @@ static int gpmi_count_bitflips(struct nand_chip *chip, void *buf, int first,
if (i == 0) {
/* The first block includes metadata */
flips = nand_check_erased_ecc_chunk(
buf + i * nfc_geo->ecc_chunk_size,
nfc_geo->ecc_chunk_size,
buf + i * nfc_geo->eccn_chunk_size,
nfc_geo->eccn_chunk_size,
eccbuf, eccbytes,
this->auxiliary_virt,
nfc_geo->metadata_size,
nfc_geo->ecc_strength);
} else {
flips = nand_check_erased_ecc_chunk(
buf + i * nfc_geo->ecc_chunk_size,
nfc_geo->ecc_chunk_size,
buf + i * nfc_geo->eccn_chunk_size,
nfc_geo->eccn_chunk_size,
eccbuf, eccbytes,
NULL, 0,
nfc_geo->ecc_strength);
@ -1310,20 +1505,21 @@ static void gpmi_bch_layout_std(struct gpmi_nand_data *this)
struct bch_geometry *geo = &this->bch_geometry;
unsigned int ecc_strength = geo->ecc_strength >> 1;
unsigned int gf_len = geo->gf_len;
unsigned int block_size = geo->ecc_chunk_size;
unsigned int block0_size = geo->ecc0_chunk_size;
unsigned int blockn_size = geo->eccn_chunk_size;
this->bch_flashlayout0 =
BF_BCH_FLASH0LAYOUT0_NBLOCKS(geo->ecc_chunk_count - 1) |
BF_BCH_FLASH0LAYOUT0_META_SIZE(geo->metadata_size) |
BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) |
BF_BCH_FLASH0LAYOUT0_GF(gf_len, this) |
BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size, this);
BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block0_size, this);
this->bch_flashlayout1 =
BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(geo->page_size) |
BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) |
BF_BCH_FLASH0LAYOUT1_GF(gf_len, this) |
BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this);
BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(blockn_size, this);
}
static int gpmi_ecc_read_page(struct nand_chip *chip, uint8_t *buf,
@ -1406,29 +1602,49 @@ static int gpmi_ecc_read_subpage(struct nand_chip *chip, uint32_t offs,
}
}
/*
* if there is an ECC dedicate for meta:
* - need to add an extra ECC size when calculating col and page_size,
* if the meta size is NOT zero.
* - ecc0_chunk size need to set to the same size as other chunks,
* if the meta size is zero.
*/
meta = geo->metadata_size;
if (first) {
col = meta + (size + ecc_parity_size) * first;
if (geo->ecc_for_meta)
col = meta + ecc_parity_size
+ (size + ecc_parity_size) * first;
else
col = meta + (size + ecc_parity_size) * first;
meta = 0;
buf = buf + first * size;
}
ecc_parity_size = geo->gf_len * geo->ecc_strength / 8;
n = last - first + 1;
page_size = meta + (size + ecc_parity_size) * n;
if (geo->ecc_for_meta && meta)
page_size = meta + ecc_parity_size
+ (size + ecc_parity_size) * n;
else
page_size = meta + (size + ecc_parity_size) * n;
ecc_strength = geo->ecc_strength >> 1;
this->bch_flashlayout0 = BF_BCH_FLASH0LAYOUT0_NBLOCKS(n - 1) |
this->bch_flashlayout0 = BF_BCH_FLASH0LAYOUT0_NBLOCKS(
(geo->ecc_for_meta ? n : n - 1)) |
BF_BCH_FLASH0LAYOUT0_META_SIZE(meta) |
BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) |
BF_BCH_FLASH0LAYOUT0_GF(geo->gf_len, this) |
BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(geo->ecc_chunk_size, this);
BF_BCH_FLASH0LAYOUT0_DATA0_SIZE((geo->ecc_for_meta ?
0 : geo->ecc0_chunk_size), this);
this->bch_flashlayout1 = BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) |
BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) |
BF_BCH_FLASH0LAYOUT1_GF(geo->gf_len, this) |
BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(geo->ecc_chunk_size, this);
BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(geo->eccn_chunk_size, this);
this->bch = true;
@ -1597,7 +1813,7 @@ static int gpmi_ecc_read_page_raw(struct nand_chip *chip, uint8_t *buf,
struct mtd_info *mtd = nand_to_mtd(chip);
struct gpmi_nand_data *this = nand_get_controller_data(chip);
struct bch_geometry *nfc_geo = &this->bch_geometry;
int eccsize = nfc_geo->ecc_chunk_size;
int eccsize = nfc_geo->eccn_chunk_size;
int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
u8 *tmp_buf = this->raw_buffer;
size_t src_bit_off;
@ -1682,7 +1898,7 @@ static int gpmi_ecc_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
struct mtd_info *mtd = nand_to_mtd(chip);
struct gpmi_nand_data *this = nand_get_controller_data(chip);
struct bch_geometry *nfc_geo = &this->bch_geometry;
int eccsize = nfc_geo->ecc_chunk_size;
int eccsize = nfc_geo->eccn_chunk_size;
int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
u8 *tmp_buf = this->raw_buffer;
uint8_t *oob = chip->oob_poi;
@ -2056,7 +2272,7 @@ static int gpmi_init_last(struct gpmi_nand_data *this)
ecc->read_oob_raw = gpmi_ecc_read_oob_raw;
ecc->write_oob_raw = gpmi_ecc_write_oob_raw;
ecc->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
ecc->size = bch_geo->ecc_chunk_size;
ecc->size = bch_geo->eccn_chunk_size;
ecc->strength = bch_geo->ecc_strength;
mtd_set_ooblayout(mtd, &gpmi_ooblayout_ops);

View file

@ -30,9 +30,9 @@ struct resources {
* @page_size: The size, in bytes, of a physical page, including
* both data and OOB.
* @metadata_size: The size, in bytes, of the metadata.
* @ecc_chunk_size: The size, in bytes, of a single ECC chunk. Note
* the first chunk in the page includes both data and
* metadata, so it's a bit larger than this value.
* @ecc0_chunk_size: The size, in bytes, of a first ECC chunk.
* @eccn_chunk_size: The size, in bytes, of a single ECC chunk after
* the first chunk in the page.
* @ecc_chunk_count: The number of ECC chunks in the page,
* @payload_size: The size, in bytes, of the payload buffer.
* @auxiliary_size: The size, in bytes, of the auxiliary buffer.
@ -42,19 +42,23 @@ struct resources {
* which the underlying physical block mark appears.
* @block_mark_bit_offset: The bit offset into the ECC-based page view at
* which the underlying physical block mark appears.
* @ecc_for_meta: The flag to indicate if there is a dedicate ecc
* for meta.
*/
struct bch_geometry {
unsigned int gf_len;
unsigned int ecc_strength;
unsigned int page_size;
unsigned int metadata_size;
unsigned int ecc_chunk_size;
unsigned int ecc0_chunk_size;
unsigned int eccn_chunk_size;
unsigned int ecc_chunk_count;
unsigned int payload_size;
unsigned int auxiliary_size;
unsigned int auxiliary_status_offset;
unsigned int block_mark_byte_offset;
unsigned int block_mark_bit_offset;
unsigned int ecc_for_meta; /* ECC for meta data */
};
/**

View file

@ -619,9 +619,9 @@ static int ebu_nand_probe(struct platform_device *pdev)
resname = devm_kasprintf(dev, GFP_KERNEL, "nand_cs%d", cs);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, resname);
ebu_host->cs[cs].chipaddr = devm_ioremap_resource(dev, res);
ebu_host->cs[cs].nand_pa = res->start;
if (IS_ERR(ebu_host->cs[cs].chipaddr))
return PTR_ERR(ebu_host->cs[cs].chipaddr);
ebu_host->cs[cs].nand_pa = res->start;
ebu_host->clk = devm_clk_get(dev, NULL);
if (IS_ERR(ebu_host->clk))

View file

@ -595,8 +595,7 @@ static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd)
struct nand_chip *chip = mtd_to_nand(mtd);
struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
if (prv->clk)
clk_disable_unprepare(prv->clk);
clk_disable_unprepare(prv->clk);
if (prv->csreg)
iounmap(prv->csreg);

View file

@ -17,7 +17,7 @@
#include <linux/iopoll.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include "mtk_ecc.h"
#include <linux/mtd/nand-ecc-mtk.h>
/* NAND controller register definition */
#define NFI_CNFG (0x00)

View file

@ -4502,11 +4502,13 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
len = instr->len;
while (len) {
loff_t ofs = (loff_t)page << chip->page_shift;
/* Check if we have a bad block, we do not erase bad blocks! */
if (nand_block_checkbad(chip, ((loff_t) page) <<
chip->page_shift, allowbbt)) {
pr_warn("%s: attempt to erase a bad block at page 0x%08x\n",
__func__, page);
pr_warn("%s: attempt to erase a bad block at 0x%08llx\n",
__func__, (unsigned long long)ofs);
ret = -EIO;
goto erase_exit;
}
@ -4524,8 +4526,7 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
if (ret) {
pr_debug("%s: failed erase, page 0x%08x\n",
__func__, page);
instr->fail_addr =
((loff_t)page << chip->page_shift);
instr->fail_addr = ofs;
goto erase_exit;
}

View file

@ -29,6 +29,9 @@ struct nand_flash_dev nand_flash_ids[] = {
{"TC58NVG0S3E 1G 3.3V 8-bit",
{ .id = {0x98, 0xd1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00} },
SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512), },
{"TC58NVG0S3HTA00 1G 3.3V 8-bit",
{ .id = {0x98, 0xf1, 0x80, 0x15} },
SZ_2K, SZ_128, SZ_128K, 0, 4, 128, NAND_ECC_INFO(8, SZ_512), },
{"TC58NVG2S0F 4G 3.3V 8-bit",
{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} },
SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) },
@ -58,6 +61,9 @@ struct nand_flash_dev nand_flash_ids[] = {
{"TH58NVG2S3HBAI4 4G 3.3V 8-bit",
{ .id = {0x98, 0xdc, 0x91, 0x15, 0x76} },
SZ_2K, SZ_512, SZ_128K, 0, 5, 128, NAND_ECC_INFO(8, SZ_512) },
{"TH58NVG3S0HBAI4 8G 3.3V 8-bit",
{ .id = {0x98, 0xd3, 0x91, 0x26, 0x76} },
SZ_4K, SZ_1K, SZ_256K, 0, 5, 256, NAND_ECC_INFO(8, SZ_512)},
LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS),
LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),

View file

@ -287,8 +287,10 @@ static int toshiba_nand_init(struct nand_chip *chip)
if (!strncmp("TC58NVG0S3E", chip->parameters.model,
sizeof("TC58NVG0S3E") - 1))
tc58nvg0s3e_init(chip);
if (!strncmp("TH58NVG2S3HBAI4", chip->parameters.model,
sizeof("TH58NVG2S3HBAI4") - 1))
if ((!strncmp("TH58NVG2S3HBAI4", chip->parameters.model,
sizeof("TH58NVG2S3HBAI4") - 1)) ||
(!strncmp("TH58NVG3S0HBAI4", chip->parameters.model,
sizeof("TH58NVG3S0HBAI4") - 1)))
th58nvg2s3hbai4_init(chip);
return 0;

View file

@ -548,6 +548,7 @@ static SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume);
#ifdef CONFIG_OF
static const struct of_device_id elm_of_match[] = {
{ .compatible = "ti,am3352-elm" },
{ .compatible = "ti,am64-elm" },
{},
};
MODULE_DEVICE_TABLE(of, elm_of_match);

View file

@ -16,6 +16,7 @@
#include <linux/mtd/rawnand.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#define COMMAND_REG 0x00
@ -216,8 +217,7 @@ struct rnandc {
struct nand_controller controller;
struct device *dev;
void __iomem *regs;
struct clk *hclk;
struct clk *eclk;
unsigned long ext_clk_rate;
unsigned long assigned_cs;
struct list_head chips;
struct nand_chip *selected_chip;
@ -891,7 +891,7 @@ static int rnandc_setup_interface(struct nand_chip *chip, int chipnr,
{
struct rnand_chip *rnand = to_rnand(chip);
struct rnandc *rnandc = to_rnandc(chip->controller);
unsigned int period_ns = 1000000000 / clk_get_rate(rnandc->eclk);
unsigned int period_ns = 1000000000 / rnandc->ext_clk_rate;
const struct nand_sdr_timings *sdr;
unsigned int cyc, cle, ale, bef_dly, ca_to_data;
@ -1319,6 +1319,7 @@ static int rnandc_chips_init(struct rnandc *rnandc)
static int rnandc_probe(struct platform_device *pdev)
{
struct rnandc *rnandc;
struct clk *eclk;
int irq, ret;
rnandc = devm_kzalloc(&pdev->dev, sizeof(*rnandc), GFP_KERNEL);
@ -1335,29 +1336,26 @@ static int rnandc_probe(struct platform_device *pdev)
if (IS_ERR(rnandc->regs))
return PTR_ERR(rnandc->regs);
/* APB clock */
rnandc->hclk = devm_clk_get(&pdev->dev, "hclk");
if (IS_ERR(rnandc->hclk))
return PTR_ERR(rnandc->hclk);
/* External NAND bus clock */
rnandc->eclk = devm_clk_get(&pdev->dev, "eclk");
if (IS_ERR(rnandc->eclk))
return PTR_ERR(rnandc->eclk);
ret = clk_prepare_enable(rnandc->hclk);
if (ret)
devm_pm_runtime_enable(&pdev->dev);
ret = pm_runtime_resume_and_get(&pdev->dev);
if (ret < 0)
return ret;
ret = clk_prepare_enable(rnandc->eclk);
if (ret)
goto disable_hclk;
/* The external NAND bus clock rate is needed for computing timings */
eclk = clk_get(&pdev->dev, "eclk");
if (IS_ERR(eclk)) {
ret = PTR_ERR(eclk);
goto dis_runtime_pm;
}
rnandc->ext_clk_rate = clk_get_rate(eclk);
clk_put(eclk);
rnandc_dis_interrupts(rnandc);
irq = platform_get_irq_optional(pdev, 0);
if (irq == -EPROBE_DEFER) {
ret = irq;
goto disable_eclk;
goto dis_runtime_pm;
} else if (irq < 0) {
dev_info(&pdev->dev, "No IRQ found, fallback to polling\n");
rnandc->use_polling = true;
@ -1365,12 +1363,12 @@ static int rnandc_probe(struct platform_device *pdev)
ret = devm_request_irq(&pdev->dev, irq, rnandc_irq_handler, 0,
"renesas-nand-controller", rnandc);
if (ret < 0)
goto disable_eclk;
goto dis_runtime_pm;
}
ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
if (ret)
goto disable_eclk;
goto dis_runtime_pm;
rnandc_clear_fifo(rnandc);
@ -1378,14 +1376,12 @@ static int rnandc_probe(struct platform_device *pdev)
ret = rnandc_chips_init(rnandc);
if (ret)
goto disable_eclk;
goto dis_runtime_pm;
return 0;
disable_eclk:
clk_disable_unprepare(rnandc->eclk);
disable_hclk:
clk_disable_unprepare(rnandc->hclk);
dis_runtime_pm:
pm_runtime_put(&pdev->dev);
return ret;
}
@ -1396,8 +1392,7 @@ static int rnandc_remove(struct platform_device *pdev)
rnandc_chips_cleanup(rnandc);
clk_disable_unprepare(rnandc->eclk);
clk_disable_unprepare(rnandc->hclk);
pm_runtime_put(&pdev->dev);
return 0;
}

View file

@ -911,8 +911,7 @@ static int rk_nfc_enable_clks(struct device *dev, struct rk_nfc *nfc)
ret = clk_prepare_enable(nfc->ahb_clk);
if (ret) {
dev_err(dev, "failed to enable ahb clk\n");
if (!IS_ERR(nfc->nfc_clk))
clk_disable_unprepare(nfc->nfc_clk);
clk_disable_unprepare(nfc->nfc_clk);
return ret;
}
@ -921,8 +920,7 @@ static int rk_nfc_enable_clks(struct device *dev, struct rk_nfc *nfc)
static void rk_nfc_disable_clks(struct rk_nfc *nfc)
{
if (!IS_ERR(nfc->nfc_clk))
clk_disable_unprepare(nfc->nfc_clk);
clk_disable_unprepare(nfc->nfc_clk);
clk_disable_unprepare(nfc->ahb_clk);
}

View file

@ -390,6 +390,9 @@ static int tmio_probe(struct platform_device *dev)
if (data == NULL)
dev_warn(&dev->dev, "NULL platform data!\n");
if (!ccr || !fcr)
return -EINVAL;
tmio = devm_kzalloc(&dev->dev, sizeof(*tmio), GFP_KERNEL);
if (!tmio)
return -ENOMEM;

View file

@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0
spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o
spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o
obj-$(CONFIG_MTD_SPI_NAND) += spinand.o

View file

@ -933,6 +933,7 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = {
&paragon_spinand_manufacturer,
&toshiba_spinand_manufacturer,
&winbond_spinand_manufacturer,
&xtx_spinand_manufacturer,
};
static int spinand_manufacturer_match(struct spinand_device *spinand,

View file

@ -39,6 +39,22 @@ static SPINAND_OP_VARIANTS(read_cache_variants_f,
SPINAND_PAGE_READ_FROM_CACHE_OP_3A(true, 0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_OP_3A(false, 0, 0, NULL, 0));
static SPINAND_OP_VARIANTS(read_cache_variants_1gq5,
SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
static SPINAND_OP_VARIANTS(read_cache_variants_2gq5,
SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 4, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 2, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
static SPINAND_OP_VARIANTS(write_cache_variants,
SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
SPINAND_PROG_LOAD(true, 0, NULL, 0));
@ -325,6 +341,36 @@ static const struct spinand_info gigadevice_spinand_table[] = {
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq4uexxg_ecc_get_status)),
SPINAND_INFO("GD5F1GQ4RExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xc1),
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq4uexxg_ecc_get_status)),
SPINAND_INFO("GD5F2GQ4UExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xd2),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq4uexxg_ecc_get_status)),
SPINAND_INFO("GD5F2GQ4RExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xc2),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq4uexxg_ecc_get_status)),
SPINAND_INFO("GD5F1GQ4UFxxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48),
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
@ -339,12 +385,122 @@ static const struct spinand_info gigadevice_spinand_table[] = {
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x51),
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(4, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq5xexxg_ecc_get_status)),
SPINAND_INFO("GD5F1GQ5RExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x41),
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(4, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq5xexxg_ecc_get_status)),
SPINAND_INFO("GD5F2GQ5UExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x52),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
NAND_ECCREQ(4, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq5xexxg_ecc_get_status)),
SPINAND_INFO("GD5F2GQ5RExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x42),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
NAND_ECCREQ(4, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq5xexxg_ecc_get_status)),
SPINAND_INFO("GD5F4GQ6UExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x55),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 2, 1),
NAND_ECCREQ(4, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq5xexxg_ecc_get_status)),
SPINAND_INFO("GD5F4GQ6RExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x45),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 2, 1),
NAND_ECCREQ(4, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq5xexxg_ecc_get_status)),
SPINAND_INFO("GD5F1GM7UExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x91),
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq4uexxg_ecc_get_status)),
SPINAND_INFO("GD5F1GM7RExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x81),
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq4uexxg_ecc_get_status)),
SPINAND_INFO("GD5F2GM7UExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq4uexxg_ecc_get_status)),
SPINAND_INFO("GD5F2GM7RExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x82),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq4uexxg_ecc_get_status)),
SPINAND_INFO("GD5F4GM8UExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x95),
NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq4uexxg_ecc_get_status)),
SPINAND_INFO("GD5F4GM8RExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x85),
NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq4uexxg_ecc_get_status)),
};
static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = {

129
drivers/mtd/nand/spi/xtx.c Normal file
View file

@ -0,0 +1,129 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Author:
* Felix Matouschek <felix@matouschek.org>
*/
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/mtd/spinand.h>
#define SPINAND_MFR_XTX 0x0B
#define XT26G0XA_STATUS_ECC_MASK GENMASK(5, 2)
#define XT26G0XA_STATUS_ECC_NO_DETECTED (0 << 2)
#define XT26G0XA_STATUS_ECC_8_CORRECTED (3 << 4)
#define XT26G0XA_STATUS_ECC_UNCOR_ERROR (2 << 4)
static SPINAND_OP_VARIANTS(read_cache_variants,
SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
static SPINAND_OP_VARIANTS(write_cache_variants,
SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
SPINAND_PROG_LOAD(true, 0, NULL, 0));
static SPINAND_OP_VARIANTS(update_cache_variants,
SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
SPINAND_PROG_LOAD(false, 0, NULL, 0));
static int xt26g0xa_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
if (section)
return -ERANGE;
region->offset = 48;
region->length = 16;
return 0;
}
static int xt26g0xa_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
if (section)
return -ERANGE;
region->offset = 1;
region->length = 47;
return 0;
}
static const struct mtd_ooblayout_ops xt26g0xa_ooblayout = {
.ecc = xt26g0xa_ooblayout_ecc,
.free = xt26g0xa_ooblayout_free,
};
static int xt26g0xa_ecc_get_status(struct spinand_device *spinand,
u8 status)
{
status = status & XT26G0XA_STATUS_ECC_MASK;
switch (status) {
case XT26G0XA_STATUS_ECC_NO_DETECTED:
return 0;
case XT26G0XA_STATUS_ECC_8_CORRECTED:
return 8;
case XT26G0XA_STATUS_ECC_UNCOR_ERROR:
return -EBADMSG;
default:
break;
}
/* At this point values greater than (2 << 4) are invalid */
if (status > XT26G0XA_STATUS_ECC_UNCOR_ERROR)
return -EINVAL;
/* (1 << 2) through (7 << 2) are 1-7 corrected errors */
return status >> 2;
}
static const struct spinand_info xtx_spinand_table[] = {
SPINAND_INFO("XT26G01A",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE1),
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&xt26g0xa_ooblayout,
xt26g0xa_ecc_get_status)),
SPINAND_INFO("XT26G02A",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE2),
NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&xt26g0xa_ooblayout,
xt26g0xa_ecc_get_status)),
SPINAND_INFO("XT26G04A",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE3),
NAND_MEMORG(1, 2048, 64, 128, 2048, 40, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&xt26g0xa_ooblayout,
xt26g0xa_ecc_get_status)),
};
static const struct spinand_manufacturer_ops xtx_spinand_manuf_ops = {
};
const struct spinand_manufacturer xtx_spinand_manufacturer = {
.id = SPINAND_MFR_XTX,
.name = "XTX",
.chips = xtx_spinand_table,
.nchips = ARRAY_SIZE(xtx_spinand_table),
.ops = &xtx_spinand_manuf_ops,
};

View file

@ -237,7 +237,7 @@ static int bcm47xxpart_parse(struct mtd_info *master,
(uint8_t *)buf);
if (err && !mtd_is_bitflip(err)) {
pr_err("mtd_read error while parsing (offset: 0x%X): %d\n",
offset, err);
offset + 0x8000, err);
continue;
}

View file

@ -17,6 +17,7 @@ spi-nor-objs += sst.o
spi-nor-objs += winbond.o
spi-nor-objs += xilinx.o
spi-nor-objs += xmc.o
spi-nor-$(CONFIG_DEBUG_FS) += debugfs.o
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o
obj-$(CONFIG_MTD_SPI_NOR) += controllers/

View file

@ -307,6 +307,52 @@ ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len,
return nor->controller_ops->write(nor, to, len, buf);
}
/**
* spi_nor_read_any_reg() - read any register from flash memory, nonvolatile or
* volatile.
* @nor: pointer to 'struct spi_nor'.
* @op: SPI memory operation. op->data.buf must be DMA-able.
* @proto: SPI protocol to use for the register operation.
*
* Return: zero on success, -errno otherwise
*/
int spi_nor_read_any_reg(struct spi_nor *nor, struct spi_mem_op *op,
enum spi_nor_protocol proto)
{
if (!nor->spimem)
return -EOPNOTSUPP;
spi_nor_spimem_setup_op(nor, op, proto);
return spi_nor_spimem_exec_op(nor, op);
}
/**
* spi_nor_write_any_volatile_reg() - write any volatile register to flash
* memory.
* @nor: pointer to 'struct spi_nor'
* @op: SPI memory operation. op->data.buf must be DMA-able.
* @proto: SPI protocol to use for the register operation.
*
* Writing volatile registers are instant according to some manufacturers
* (Cypress, Micron) and do not need any status polling.
*
* Return: zero on success, -errno otherwise
*/
int spi_nor_write_any_volatile_reg(struct spi_nor *nor, struct spi_mem_op *op,
enum spi_nor_protocol proto)
{
int ret;
if (!nor->spimem)
return -EOPNOTSUPP;
ret = spi_nor_write_enable(nor);
if (ret)
return ret;
spi_nor_spimem_setup_op(nor, op, proto);
return spi_nor_spimem_exec_op(nor, op);
}
/**
* spi_nor_write_enable() - Set write enable latch with Write Enable command.
* @nor: pointer to 'struct spi_nor'.
@ -318,11 +364,7 @@ int spi_nor_write_enable(struct spi_nor *nor)
int ret;
if (nor->spimem) {
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREN, 0),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_NO_DATA);
struct spi_mem_op op = SPI_NOR_WREN_OP;
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
@ -349,11 +391,7 @@ int spi_nor_write_disable(struct spi_nor *nor)
int ret;
if (nor->spimem) {
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRDI, 0),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_NO_DATA);
struct spi_mem_op op = SPI_NOR_WRDI_OP;
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
@ -369,6 +407,37 @@ int spi_nor_write_disable(struct spi_nor *nor)
return ret;
}
/**
* spi_nor_read_id() - Read the JEDEC ID.
* @nor: pointer to 'struct spi_nor'.
* @naddr: number of address bytes to send. Can be zero if the operation
* does not need to send an address.
* @ndummy: number of dummy bytes to send after an opcode or address. Can
* be zero if the operation does not require dummy bytes.
* @id: pointer to a DMA-able buffer where the value of the JEDEC ID
* will be written.
* @proto: the SPI protocol for register operation.
*
* Return: 0 on success, -errno otherwise.
*/
int spi_nor_read_id(struct spi_nor *nor, u8 naddr, u8 ndummy, u8 *id,
enum spi_nor_protocol proto)
{
int ret;
if (nor->spimem) {
struct spi_mem_op op =
SPI_NOR_READID_OP(naddr, ndummy, id, SPI_NOR_MAX_ID_LEN);
spi_nor_spimem_setup_op(nor, &op, proto);
ret = spi_mem_exec_op(nor->spimem, &op);
} else {
ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDID, id,
SPI_NOR_MAX_ID_LEN);
}
return ret;
}
/**
* spi_nor_read_sr() - Read the Status Register.
* @nor: pointer to 'struct spi_nor'.
@ -382,11 +451,7 @@ int spi_nor_read_sr(struct spi_nor *nor, u8 *sr)
int ret;
if (nor->spimem) {
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 0),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(1, sr, 0));
struct spi_mem_op op = SPI_NOR_RDSR_OP(sr);
if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) {
op.addr.nbytes = nor->params->rdsr_addr_nbytes;
@ -426,11 +491,7 @@ int spi_nor_read_cr(struct spi_nor *nor, u8 *cr)
int ret;
if (nor->spimem) {
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDCR, 0),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(1, cr, 0));
struct spi_mem_op op = SPI_NOR_RDCR_OP(cr);
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
@ -459,14 +520,7 @@ int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
int ret;
if (nor->spimem) {
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(enable ?
SPINOR_OP_EN4B :
SPINOR_OP_EX4B,
0),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_NO_DATA);
struct spi_mem_op op = SPI_NOR_EN4B_EX4B_OP(enable);
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
@ -500,11 +554,7 @@ static int spansion_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
nor->bouncebuf[0] = enable << 7;
if (nor->spimem) {
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_BRWR, 0),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 0));
struct spi_mem_op op = SPI_NOR_BRWR_OP(nor->bouncebuf);
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
@ -520,40 +570,6 @@ static int spansion_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
return ret;
}
/**
* spi_nor_write_ear() - Write Extended Address Register.
* @nor: pointer to 'struct spi_nor'.
* @ear: value to write to the Extended Address Register.
*
* Return: 0 on success, -errno otherwise.
*/
int spi_nor_write_ear(struct spi_nor *nor, u8 ear)
{
int ret;
nor->bouncebuf[0] = ear;
if (nor->spimem) {
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREAR, 0),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 0));
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
ret = spi_mem_exec_op(nor->spimem, &op);
} else {
ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_WREAR,
nor->bouncebuf, 1);
}
if (ret)
dev_dbg(nor->dev, "error %d writing EAR\n", ret);
return ret;
}
/**
* spi_nor_sr_ready() - Query the Status Register to see if the flash is ready
* for new commands.
@ -649,11 +665,7 @@ int spi_nor_global_block_unlock(struct spi_nor *nor)
return ret;
if (nor->spimem) {
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_GBULK, 0),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_NO_DATA);
struct spi_mem_op op = SPI_NOR_GBULK_OP;
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
@ -688,11 +700,7 @@ int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len)
return ret;
if (nor->spimem) {
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 0),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(len, sr, 0));
struct spi_mem_op op = SPI_NOR_WRSR_OP(sr, len);
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
@ -788,6 +796,15 @@ static int spi_nor_write_16bit_sr_and_check(struct spi_nor *nor, u8 sr1)
if (ret)
return ret;
ret = spi_nor_read_sr(nor, sr_cr);
if (ret)
return ret;
if (sr1 != sr_cr[0]) {
dev_dbg(nor->dev, "SR: Read back test failed\n");
return -EIO;
}
if (nor->flags & SNOR_F_NO_READ_CR)
return 0;
@ -892,11 +909,7 @@ static int spi_nor_write_sr2(struct spi_nor *nor, const u8 *sr2)
return ret;
if (nor->spimem) {
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR2, 0),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(1, sr2, 0));
struct spi_mem_op op = SPI_NOR_WRSR2_OP(sr2);
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
@ -928,11 +941,7 @@ static int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2)
int ret;
if (nor->spimem) {
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR2, 0),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(1, sr2, 0));
struct spi_mem_op op = SPI_NOR_RDSR2_OP(sr2);
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
@ -961,11 +970,7 @@ static int spi_nor_erase_chip(struct spi_nor *nor)
dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd.size >> 10));
if (nor->spimem) {
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CHIP_ERASE, 0),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_NO_DATA);
struct spi_mem_op op = SPI_NOR_CHIP_ERASE_OP;
spi_nor_spimem_setup_op(nor, &op, nor->write_proto);
@ -1107,10 +1112,8 @@ int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
if (nor->spimem) {
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(nor->erase_opcode, 0),
SPI_MEM_OP_ADDR(nor->addr_width, addr, 0),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_NO_DATA);
SPI_NOR_SECTOR_ERASE_OP(nor->erase_opcode,
nor->addr_width, addr);
spi_nor_spimem_setup_op(nor, &op, nor->write_proto);
@ -1629,58 +1632,45 @@ static const struct spi_nor_manufacturer *manufacturers[] = {
&spi_nor_xmc,
};
static const struct flash_info *
spi_nor_search_part_by_id(const struct flash_info *parts, unsigned int nparts,
const u8 *id)
static const struct flash_info *spi_nor_match_id(struct spi_nor *nor,
const u8 *id)
{
unsigned int i;
const struct flash_info *part;
unsigned int i, j;
for (i = 0; i < nparts; i++) {
if (parts[i].id_len &&
!memcmp(parts[i].id, id, parts[i].id_len))
return &parts[i];
for (i = 0; i < ARRAY_SIZE(manufacturers); i++) {
for (j = 0; j < manufacturers[i]->nparts; j++) {
part = &manufacturers[i]->parts[j];
if (part->id_len &&
!memcmp(part->id, id, part->id_len)) {
nor->manufacturer = manufacturers[i];
return part;
}
}
}
return NULL;
}
static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
static const struct flash_info *spi_nor_detect(struct spi_nor *nor)
{
const struct flash_info *info;
u8 *id = nor->bouncebuf;
unsigned int i;
int ret;
if (nor->spimem) {
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(SPI_NOR_MAX_ID_LEN, id, 1));
ret = spi_mem_exec_op(nor->spimem, &op);
} else {
ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDID, id,
SPI_NOR_MAX_ID_LEN);
}
ret = spi_nor_read_id(nor, 0, 0, id, nor->reg_proto);
if (ret) {
dev_dbg(nor->dev, "error %d reading JEDEC ID\n", ret);
return ERR_PTR(ret);
}
for (i = 0; i < ARRAY_SIZE(manufacturers); i++) {
info = spi_nor_search_part_by_id(manufacturers[i]->parts,
manufacturers[i]->nparts,
id);
if (info) {
nor->manufacturer = manufacturers[i];
return info;
}
info = spi_nor_match_id(nor, id);
if (!info) {
dev_err(nor->dev, "unrecognized JEDEC id bytes: %*ph\n",
SPI_NOR_MAX_ID_LEN, id);
return ERR_PTR(-ENODEV);
}
dev_err(nor->dev, "unrecognized JEDEC id bytes: %*ph\n",
SPI_NOR_MAX_ID_LEN, id);
return ERR_PTR(-ENODEV);
return info;
}
static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
@ -1860,7 +1850,7 @@ int spi_nor_hwcaps_read2cmd(u32 hwcaps)
ARRAY_SIZE(hwcaps_read2cmd));
}
static int spi_nor_hwcaps_pp2cmd(u32 hwcaps)
int spi_nor_hwcaps_pp2cmd(u32 hwcaps)
{
static const int hwcaps_pp2cmd[][2] = {
{ SNOR_HWCAPS_PP, SNOR_CMD_PP },
@ -1919,10 +1909,7 @@ static int spi_nor_spimem_check_op(struct spi_nor *nor,
static int spi_nor_spimem_check_readop(struct spi_nor *nor,
const struct spi_nor_read_command *read)
{
struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(read->opcode, 0),
SPI_MEM_OP_ADDR(3, 0, 0),
SPI_MEM_OP_DUMMY(1, 0),
SPI_MEM_OP_DATA_IN(2, NULL, 0));
struct spi_mem_op op = SPI_NOR_READ_OP(read->opcode);
spi_nor_spimem_setup_op(nor, &op, read->proto);
@ -1945,10 +1932,7 @@ static int spi_nor_spimem_check_readop(struct spi_nor *nor,
static int spi_nor_spimem_check_pp(struct spi_nor *nor,
const struct spi_nor_pp_command *pp)
{
struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(pp->opcode, 0),
SPI_MEM_OP_ADDR(3, 0, 0),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(2, NULL, 0));
struct spi_mem_op op = SPI_NOR_PP_OP(pp->opcode);
spi_nor_spimem_setup_op(nor, &op, pp->proto);
@ -2772,10 +2756,7 @@ static void spi_nor_soft_reset(struct spi_nor *nor)
struct spi_mem_op op;
int ret;
op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SRSTEN, 0),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DATA);
op = (struct spi_mem_op)SPINOR_SRSTEN_OP;
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
@ -2785,10 +2766,7 @@ static void spi_nor_soft_reset(struct spi_nor *nor)
return;
}
op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SRST, 0),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DATA);
op = (struct spi_mem_op)SPINOR_SRST_OP;
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
@ -2876,8 +2854,8 @@ void spi_nor_restore(struct spi_nor *nor)
}
EXPORT_SYMBOL_GPL(spi_nor_restore);
static const struct flash_info *spi_nor_match_id(struct spi_nor *nor,
const char *name)
static const struct flash_info *spi_nor_match_name(struct spi_nor *nor,
const char *name)
{
unsigned int i, j;
@ -2899,12 +2877,10 @@ static const struct flash_info *spi_nor_get_flash_info(struct spi_nor *nor,
const struct flash_info *info = NULL;
if (name)
info = spi_nor_match_id(nor, name);
info = spi_nor_match_name(nor, name);
/* Try to auto-detect if chip name wasn't specified or not found */
if (!info)
info = spi_nor_read_id(nor);
if (IS_ERR_OR_NULL(info))
return ERR_PTR(-ENOENT);
return spi_nor_detect(nor);
/*
* If caller has specified name of flash model that can normally be
@ -2913,7 +2889,7 @@ static const struct flash_info *spi_nor_get_flash_info(struct spi_nor *nor,
if (name && info->id_len) {
const struct flash_info *jinfo;
jinfo = spi_nor_read_id(nor);
jinfo = spi_nor_detect(nor);
if (IS_ERR(jinfo)) {
return jinfo;
} else if (jinfo != info) {
@ -3156,6 +3132,8 @@ static int spi_nor_probe(struct spi_mem *spimem)
if (ret)
return ret;
spi_nor_debugfs_register(nor);
/*
* None of the existing parts have > 512B pages, but let's play safe
* and add this logic so that if anyone ever adds support for such

View file

@ -11,6 +11,110 @@
#define SPI_NOR_MAX_ID_LEN 6
/* Standard SPI NOR flash operations. */
#define SPI_NOR_READID_OP(naddr, ndummy, buf, len) \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 0), \
SPI_MEM_OP_ADDR(naddr, 0, 0), \
SPI_MEM_OP_DUMMY(ndummy, 0), \
SPI_MEM_OP_DATA_IN(len, buf, 0))
#define SPI_NOR_WREN_OP \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREN, 0), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPI_NOR_WRDI_OP \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRDI, 0), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPI_NOR_RDSR_OP(buf) \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 0), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_IN(1, buf, 0))
#define SPI_NOR_WRSR_OP(buf, len) \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 0), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(len, buf, 0))
#define SPI_NOR_RDSR2_OP(buf) \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR2, 0), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(1, buf, 0))
#define SPI_NOR_WRSR2_OP(buf) \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR2, 0), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(1, buf, 0))
#define SPI_NOR_RDCR_OP(buf) \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDCR, 0), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_IN(1, buf, 0))
#define SPI_NOR_EN4B_EX4B_OP(enable) \
SPI_MEM_OP(SPI_MEM_OP_CMD(enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B, 0), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPI_NOR_BRWR_OP(buf) \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_BRWR, 0), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(1, buf, 0))
#define SPI_NOR_GBULK_OP \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_GBULK, 0), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPI_NOR_CHIP_ERASE_OP \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CHIP_ERASE, 0), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPI_NOR_SECTOR_ERASE_OP(opcode, addr_width, addr) \
SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \
SPI_MEM_OP_ADDR(addr_width, addr, 0), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPI_NOR_READ_OP(opcode) \
SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \
SPI_MEM_OP_ADDR(3, 0, 0), \
SPI_MEM_OP_DUMMY(1, 0), \
SPI_MEM_OP_DATA_IN(2, NULL, 0))
#define SPI_NOR_PP_OP(opcode) \
SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \
SPI_MEM_OP_ADDR(3, 0, 0), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(2, NULL, 0))
#define SPINOR_SRSTEN_OP \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SRSTEN, 0), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DATA)
#define SPINOR_SRST_OP \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SRST, 0), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DATA)
/* Keep these in sync with the list in debugfs.c */
enum spi_nor_option_flags {
SNOR_F_HAS_SR_TB = BIT(0),
SNOR_F_NO_OP_CHIP_ERASE = BIT(1),
@ -236,9 +340,10 @@ struct spi_nor_otp {
* @writesize Minimal writable flash unit size. Defaults to 1. Set to
* ECC unit size for ECC-ed flashes.
* @page_size: the page size of the SPI NOR flash memory.
* @rdsr_dummy: dummy cycles needed for Read Status Register command.
* @rdsr_dummy: dummy cycles needed for Read Status Register command
* in octal DTR mode.
* @rdsr_addr_nbytes: dummy address bytes needed for Read Status Register
* command.
* command in octal DTR mode.
* @hwcaps: describes the read and page program hardware
* capabilities.
* @reads: read capabilities ordered by priority: the higher index
@ -526,7 +631,6 @@ void spi_nor_spimem_setup_op(const struct spi_nor *nor,
int spi_nor_write_enable(struct spi_nor *nor);
int spi_nor_write_disable(struct spi_nor *nor);
int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable);
int spi_nor_write_ear(struct spi_nor *nor, u8 ear);
int spi_nor_wait_till_ready(struct spi_nor *nor);
int spi_nor_global_block_unlock(struct spi_nor *nor);
int spi_nor_lock_and_prep(struct spi_nor *nor);
@ -534,6 +638,8 @@ void spi_nor_unlock_and_unprep(struct spi_nor *nor);
int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor);
int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor);
int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor);
int spi_nor_read_id(struct spi_nor *nor, u8 naddr, u8 ndummy, u8 *id,
enum spi_nor_protocol reg_proto);
int spi_nor_read_sr(struct spi_nor *nor, u8 *sr);
int spi_nor_sr_ready(struct spi_nor *nor);
int spi_nor_read_cr(struct spi_nor *nor, u8 *cr);
@ -545,6 +651,10 @@ ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len,
u8 *buf);
ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len,
const u8 *buf);
int spi_nor_read_any_reg(struct spi_nor *nor, struct spi_mem_op *op,
enum spi_nor_protocol proto);
int spi_nor_write_any_volatile_reg(struct spi_nor *nor, struct spi_mem_op *op,
enum spi_nor_protocol proto);
int spi_nor_erase_sector(struct spi_nor *nor, u32 addr);
int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf);
@ -555,6 +665,7 @@ int spi_nor_otp_lock_sr2(struct spi_nor *nor, unsigned int region);
int spi_nor_otp_is_locked_sr2(struct spi_nor *nor, unsigned int region);
int spi_nor_hwcaps_read2cmd(u32 hwcaps);
int spi_nor_hwcaps_pp2cmd(u32 hwcaps);
u8 spi_nor_convert_3to4_read(u8 opcode);
void spi_nor_set_read_settings(struct spi_nor_read_command *read,
u8 num_mode_clocks,
@ -590,4 +701,10 @@ static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
return container_of(mtd, struct spi_nor, mtd);
}
#ifdef CONFIG_DEBUG_FS
void spi_nor_debugfs_register(struct spi_nor *nor);
#else
static inline void spi_nor_debugfs_register(struct spi_nor *nor) {}
#endif
#endif /* __LINUX_MTD_SPI_NOR_INTERNAL_H */

View file

@ -0,0 +1,249 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/mtd/spi-nor.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
#include <linux/debugfs.h>
#include "core.h"
#define SPI_NOR_DEBUGFS_ROOT "spi-nor"
#define SNOR_F_NAME(name) [ilog2(SNOR_F_##name)] = #name
static const char *const snor_f_names[] = {
SNOR_F_NAME(HAS_SR_TB),
SNOR_F_NAME(NO_OP_CHIP_ERASE),
SNOR_F_NAME(BROKEN_RESET),
SNOR_F_NAME(4B_OPCODES),
SNOR_F_NAME(HAS_4BAIT),
SNOR_F_NAME(HAS_LOCK),
SNOR_F_NAME(HAS_16BIT_SR),
SNOR_F_NAME(NO_READ_CR),
SNOR_F_NAME(HAS_SR_TB_BIT6),
SNOR_F_NAME(HAS_4BIT_BP),
SNOR_F_NAME(HAS_SR_BP3_BIT6),
SNOR_F_NAME(IO_MODE_EN_VOLATILE),
SNOR_F_NAME(SOFT_RESET),
SNOR_F_NAME(SWP_IS_VOLATILE),
};
#undef SNOR_F_NAME
static const char *spi_nor_protocol_name(enum spi_nor_protocol proto)
{
switch (proto) {
case SNOR_PROTO_1_1_1: return "1S-1S-1S";
case SNOR_PROTO_1_1_2: return "1S-1S-2S";
case SNOR_PROTO_1_1_4: return "1S-1S-4S";
case SNOR_PROTO_1_1_8: return "1S-1S-8S";
case SNOR_PROTO_1_2_2: return "1S-2S-2S";
case SNOR_PROTO_1_4_4: return "1S-4S-4S";
case SNOR_PROTO_1_8_8: return "1S-8S-8S";
case SNOR_PROTO_2_2_2: return "2S-2S-2S";
case SNOR_PROTO_4_4_4: return "4S-4S-4S";
case SNOR_PROTO_8_8_8: return "8S-8S-8S";
case SNOR_PROTO_1_1_1_DTR: return "1D-1D-1D";
case SNOR_PROTO_1_2_2_DTR: return "1D-2D-2D";
case SNOR_PROTO_1_4_4_DTR: return "1D-4D-4D";
case SNOR_PROTO_1_8_8_DTR: return "1D-8D-8D";
case SNOR_PROTO_8_8_8_DTR: return "8D-8D-8D";
}
return "<unknown>";
}
static void spi_nor_print_flags(struct seq_file *s, unsigned long flags,
const char *const *names, int names_len)
{
bool sep = false;
int i;
for (i = 0; i < sizeof(flags) * BITS_PER_BYTE; i++) {
if (!(flags & BIT(i)))
continue;
if (sep)
seq_puts(s, " | ");
sep = true;
if (i < names_len && names[i])
seq_puts(s, names[i]);
else
seq_printf(s, "1<<%d", i);
}
}
static int spi_nor_params_show(struct seq_file *s, void *data)
{
struct spi_nor *nor = s->private;
struct spi_nor_flash_parameter *params = nor->params;
struct spi_nor_erase_map *erase_map = &params->erase_map;
struct spi_nor_erase_region *region;
const struct flash_info *info = nor->info;
char buf[16], *str;
int i;
seq_printf(s, "name\t\t%s\n", info->name);
seq_printf(s, "id\t\t%*ph\n", info->id_len, info->id);
string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf));
seq_printf(s, "size\t\t%s\n", buf);
seq_printf(s, "write size\t%u\n", params->writesize);
seq_printf(s, "page size\t%u\n", params->page_size);
seq_printf(s, "address width\t%u\n", nor->addr_width);
seq_puts(s, "flags\t\t");
spi_nor_print_flags(s, nor->flags, snor_f_names, sizeof(snor_f_names));
seq_puts(s, "\n");
seq_puts(s, "\nopcodes\n");
seq_printf(s, " read\t\t0x%02x\n", nor->read_opcode);
seq_printf(s, " dummy cycles\t%u\n", nor->read_dummy);
seq_printf(s, " erase\t\t0x%02x\n", nor->erase_opcode);
seq_printf(s, " program\t0x%02x\n", nor->program_opcode);
switch (nor->cmd_ext_type) {
case SPI_NOR_EXT_NONE:
str = "none";
break;
case SPI_NOR_EXT_REPEAT:
str = "repeat";
break;
case SPI_NOR_EXT_INVERT:
str = "invert";
break;
default:
str = "<unknown>";
break;
}
seq_printf(s, " 8D extension\t%s\n", str);
seq_puts(s, "\nprotocols\n");
seq_printf(s, " read\t\t%s\n",
spi_nor_protocol_name(nor->read_proto));
seq_printf(s, " write\t\t%s\n",
spi_nor_protocol_name(nor->write_proto));
seq_printf(s, " register\t%s\n",
spi_nor_protocol_name(nor->reg_proto));
seq_puts(s, "\nerase commands\n");
for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) {
struct spi_nor_erase_type *et = &erase_map->erase_type[i];
if (et->size) {
string_get_size(et->size, 1, STRING_UNITS_2, buf,
sizeof(buf));
seq_printf(s, " %02x (%s) [%d]\n", et->opcode, buf, i);
}
}
if (!(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf));
seq_printf(s, " %02x (%s)\n", SPINOR_OP_CHIP_ERASE, buf);
}
seq_puts(s, "\nsector map\n");
seq_puts(s, " region (in hex) | erase mask | flags\n");
seq_puts(s, " ------------------+------------+----------\n");
for (region = erase_map->regions;
region;
region = spi_nor_region_next(region)) {
u64 start = region->offset & ~SNOR_ERASE_FLAGS_MASK;
u64 flags = region->offset & SNOR_ERASE_FLAGS_MASK;
u64 end = start + region->size - 1;
seq_printf(s, " %08llx-%08llx | [%c%c%c%c] | %s\n",
start, end,
flags & BIT(0) ? '0' : ' ',
flags & BIT(1) ? '1' : ' ',
flags & BIT(2) ? '2' : ' ',
flags & BIT(3) ? '3' : ' ',
flags & SNOR_OVERLAID_REGION ? "overlaid" : "");
}
return 0;
}
DEFINE_SHOW_ATTRIBUTE(spi_nor_params);
static void spi_nor_print_read_cmd(struct seq_file *s, u32 cap,
struct spi_nor_read_command *cmd)
{
seq_printf(s, " %s%s\n", spi_nor_protocol_name(cmd->proto),
cap == SNOR_HWCAPS_READ_FAST ? " (fast read)" : "");
seq_printf(s, " opcode\t0x%02x\n", cmd->opcode);
seq_printf(s, " mode cycles\t%u\n", cmd->num_mode_clocks);
seq_printf(s, " dummy cycles\t%u\n", cmd->num_wait_states);
}
static void spi_nor_print_pp_cmd(struct seq_file *s,
struct spi_nor_pp_command *cmd)
{
seq_printf(s, " %s\n", spi_nor_protocol_name(cmd->proto));
seq_printf(s, " opcode\t0x%02x\n", cmd->opcode);
}
static int spi_nor_capabilities_show(struct seq_file *s, void *data)
{
struct spi_nor *nor = s->private;
struct spi_nor_flash_parameter *params = nor->params;
u32 hwcaps = params->hwcaps.mask;
int i, cmd;
seq_puts(s, "Supported read modes by the flash\n");
for (i = 0; i < sizeof(hwcaps) * BITS_PER_BYTE; i++) {
if (!(hwcaps & BIT(i)))
continue;
cmd = spi_nor_hwcaps_read2cmd(BIT(i));
if (cmd < 0)
continue;
spi_nor_print_read_cmd(s, BIT(i), &params->reads[cmd]);
hwcaps &= ~BIT(i);
}
seq_puts(s, "\nSupported page program modes by the flash\n");
for (i = 0; i < sizeof(hwcaps) * BITS_PER_BYTE; i++) {
if (!(hwcaps & BIT(i)))
continue;
cmd = spi_nor_hwcaps_pp2cmd(BIT(i));
if (cmd < 0)
continue;
spi_nor_print_pp_cmd(s, &params->page_programs[cmd]);
hwcaps &= ~BIT(i);
}
if (hwcaps)
seq_printf(s, "\nunknown hwcaps 0x%x\n", hwcaps);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(spi_nor_capabilities);
static void spi_nor_debugfs_unregister(void *data)
{
struct spi_nor *nor = data;
debugfs_remove(nor->debugfs_root);
nor->debugfs_root = NULL;
}
void spi_nor_debugfs_register(struct spi_nor *nor)
{
struct dentry *rootdir, *d;
int ret;
/* Create rootdir once. Will never be deleted again. */
rootdir = debugfs_lookup(SPI_NOR_DEBUGFS_ROOT, NULL);
if (!rootdir)
rootdir = debugfs_create_dir(SPI_NOR_DEBUGFS_ROOT, NULL);
ret = devm_add_action(nor->dev, spi_nor_debugfs_unregister, nor);
if (ret)
return;
d = debugfs_create_dir(dev_name(nor->dev), rootdir);
nor->debugfs_root = d;
debugfs_create_file("params", 0444, d, nor, &spi_nor_params_fops);
debugfs_create_file("capabilities", 0444, d, nor,
&spi_nor_capabilities_fops);
}

View file

@ -25,7 +25,8 @@ static const struct flash_info eon_nor_parts[] = {
{ "en25qh64", INFO(0x1c7017, 0, 64 * 1024, 128)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ) },
{ "en25qh128", INFO(0x1c7018, 0, 64 * 1024, 256) },
{ "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512) },
{ "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512)
PARSE_SFDP },
{ "en25s64", INFO(0x1c3817, 0, 64 * 1024, 128)
NO_SFDP_FLAGS(SECT_4K) },
};

View file

@ -28,82 +28,52 @@
#define FSR_P_ERR BIT(4) /* Program operation status */
#define FSR_PT_ERR BIT(1) /* Protection error bit */
static int micron_st_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
/* Micron ST SPI NOR flash operations. */
#define MICRON_ST_NOR_WR_ANY_REG_OP(naddr, addr, ndata, buf) \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 0), \
SPI_MEM_OP_ADDR(naddr, addr, 0), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(ndata, buf, 0))
#define MICRON_ST_RDFSR_OP(buf) \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 0), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_IN(1, buf, 0))
#define MICRON_ST_CLFSR_OP \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 0), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
static int micron_st_nor_octal_dtr_en(struct spi_nor *nor)
{
struct spi_mem_op op;
u8 *buf = nor->bouncebuf;
int ret;
if (enable) {
/* Use 20 dummy cycles for memory array reads. */
ret = spi_nor_write_enable(nor);
if (ret)
return ret;
*buf = 20;
op = (struct spi_mem_op)
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1),
SPI_MEM_OP_ADDR(3, SPINOR_REG_MT_CFR1V, 1),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(1, buf, 1));
ret = spi_mem_exec_op(nor->spimem, &op);
if (ret)
return ret;
ret = spi_nor_wait_till_ready(nor);
if (ret)
return ret;
}
ret = spi_nor_write_enable(nor);
/* Use 20 dummy cycles for memory array reads. */
*buf = 20;
op = (struct spi_mem_op)
MICRON_ST_NOR_WR_ANY_REG_OP(3, SPINOR_REG_MT_CFR1V, 1, buf);
ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
if (ret)
return ret;
if (enable) {
buf[0] = SPINOR_MT_OCT_DTR;
} else {
/*
* The register is 1-byte wide, but 1-byte transactions are not
* allowed in 8D-8D-8D mode. The next register is the dummy
* cycle configuration register. Since the transaction needs to
* be at least 2 bytes wide, set the next register to its
* default value. This also makes sense because the value was
* changed when enabling 8D-8D-8D mode, it should be reset when
* disabling.
*/
buf[0] = SPINOR_MT_EXSPI;
buf[1] = SPINOR_REG_MT_CFR1V_DEF;
}
buf[0] = SPINOR_MT_OCT_DTR;
op = (struct spi_mem_op)
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1),
SPI_MEM_OP_ADDR(enable ? 3 : 4,
SPINOR_REG_MT_CFR0V, 1),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(enable ? 1 : 2, buf, 1));
if (!enable)
spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR);
ret = spi_mem_exec_op(nor->spimem, &op);
MICRON_ST_NOR_WR_ANY_REG_OP(3, SPINOR_REG_MT_CFR0V, 1, buf);
ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
if (ret)
return ret;
/* Read flash ID to make sure the switch was successful. */
op = (struct spi_mem_op)
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_DUMMY(enable ? 8 : 0, 1),
SPI_MEM_OP_DATA_IN(round_up(nor->info->id_len, 2),
buf, 1));
if (enable)
spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR);
ret = spi_mem_exec_op(nor->spimem, &op);
if (ret)
ret = spi_nor_read_id(nor, 0, 8, buf, SNOR_PROTO_8_8_8_DTR);
if (ret) {
dev_dbg(nor->dev, "error %d reading JEDEC ID after enabling 8D-8D-8D mode\n", ret);
return ret;
}
if (memcmp(buf, nor->info->id, nor->info->id_len))
return -EINVAL;
@ -111,6 +81,47 @@ static int micron_st_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
return 0;
}
static int micron_st_nor_octal_dtr_dis(struct spi_nor *nor)
{
struct spi_mem_op op;
u8 *buf = nor->bouncebuf;
int ret;
/*
* The register is 1-byte wide, but 1-byte transactions are not allowed
* in 8D-8D-8D mode. The next register is the dummy cycle configuration
* register. Since the transaction needs to be at least 2 bytes wide,
* set the next register to its default value. This also makes sense
* because the value was changed when enabling 8D-8D-8D mode, it should
* be reset when disabling.
*/
buf[0] = SPINOR_MT_EXSPI;
buf[1] = SPINOR_REG_MT_CFR1V_DEF;
op = (struct spi_mem_op)
MICRON_ST_NOR_WR_ANY_REG_OP(4, SPINOR_REG_MT_CFR0V, 2, buf);
ret = spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8_DTR);
if (ret)
return ret;
/* Read flash ID to make sure the switch was successful. */
ret = spi_nor_read_id(nor, 0, 0, buf, SNOR_PROTO_1_1_1);
if (ret) {
dev_dbg(nor->dev, "error %d reading JEDEC ID after disabling 8D-8D-8D mode\n", ret);
return ret;
}
if (memcmp(buf, nor->info->id, nor->info->id_len))
return -EINVAL;
return 0;
}
static int micron_st_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
{
return enable ? micron_st_nor_octal_dtr_en(nor) :
micron_st_nor_octal_dtr_dis(nor);
}
static void mt35xu512aba_default_init(struct spi_nor *nor)
{
nor->params->octal_dtr_enable = micron_st_nor_octal_dtr_enable;
@ -322,11 +333,7 @@ static int micron_st_nor_read_fsr(struct spi_nor *nor, u8 *fsr)
int ret;
if (nor->spimem) {
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 0),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(1, fsr, 0));
struct spi_mem_op op = MICRON_ST_RDFSR_OP(fsr);
if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) {
op.addr.nbytes = nor->params->rdsr_addr_nbytes;
@ -361,11 +368,7 @@ static void micron_st_nor_clear_fsr(struct spi_nor *nor)
int ret;
if (nor->spimem) {
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 0),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_NO_DATA);
struct spi_mem_op op = MICRON_ST_CLFSR_OP;
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);

View file

@ -23,6 +23,96 @@
#define SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS 0
#define SPINOR_OP_CYPRESS_RD_FAST 0xee
/* Cypress SPI NOR flash operations. */
#define CYPRESS_NOR_WR_ANY_REG_OP(naddr, addr, ndata, buf) \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 0), \
SPI_MEM_OP_ADDR(naddr, addr, 0), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(ndata, buf, 0))
#define CYPRESS_NOR_RD_ANY_REG_OP(naddr, addr, buf) \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 0), \
SPI_MEM_OP_ADDR(naddr, addr, 0), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_IN(1, buf, 0))
#define SPANSION_CLSR_OP \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 0), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
static int cypress_nor_octal_dtr_en(struct spi_nor *nor)
{
struct spi_mem_op op;
u8 *buf = nor->bouncebuf;
int ret;
/* Use 24 dummy cycles for memory array reads. */
*buf = SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24;
op = (struct spi_mem_op)
CYPRESS_NOR_WR_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR2V, 1, buf);
ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
if (ret)
return ret;
nor->read_dummy = 24;
/* Set the octal and DTR enable bits. */
buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN;
op = (struct spi_mem_op)
CYPRESS_NOR_WR_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR5V, 1, buf);
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);
if (ret) {
dev_dbg(nor->dev, "error %d reading JEDEC ID after enabling 8D-8D-8D mode\n", ret);
return ret;
}
if (memcmp(buf, nor->info->id, nor->info->id_len))
return -EINVAL;
return 0;
}
static int cypress_nor_octal_dtr_dis(struct spi_nor *nor)
{
struct spi_mem_op op;
u8 *buf = nor->bouncebuf;
int ret;
/*
* The register is 1-byte wide, but 1-byte transactions are not allowed
* in 8D-8D-8D mode. Since there is no register at the next location,
* just initialize the value to 0 and let the transaction go on.
*/
buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS;
buf[1] = 0;
op = (struct spi_mem_op)
CYPRESS_NOR_WR_ANY_REG_OP(4, SPINOR_REG_CYPRESS_CFR5V, 2, buf);
ret = spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8_DTR);
if (ret)
return ret;
/* Read flash ID to make sure the switch was successful. */
ret = spi_nor_read_id(nor, 0, 0, buf, SNOR_PROTO_1_1_1);
if (ret) {
dev_dbg(nor->dev, "error %d reading JEDEC ID after disabling 8D-8D-8D mode\n", ret);
return ret;
}
if (memcmp(buf, nor->info->id, nor->info->id_len))
return -EINVAL;
return 0;
}
/**
* cypress_nor_octal_dtr_enable() - Enable octal DTR on Cypress flashes.
* @nor: pointer to a 'struct spi_nor'
@ -35,87 +125,8 @@
*/
static int cypress_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
{
struct spi_mem_op op;
u8 *buf = nor->bouncebuf;
int ret;
if (enable) {
/* Use 24 dummy cycles for memory array reads. */
ret = spi_nor_write_enable(nor);
if (ret)
return ret;
*buf = SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24;
op = (struct spi_mem_op)
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1),
SPI_MEM_OP_ADDR(3, SPINOR_REG_CYPRESS_CFR2V,
1),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(1, buf, 1));
ret = spi_mem_exec_op(nor->spimem, &op);
if (ret)
return ret;
ret = spi_nor_wait_till_ready(nor);
if (ret)
return ret;
nor->read_dummy = 24;
}
/* Set/unset the octal and DTR enable bits. */
ret = spi_nor_write_enable(nor);
if (ret)
return ret;
if (enable) {
buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN;
} else {
/*
* The register is 1-byte wide, but 1-byte transactions are not
* allowed in 8D-8D-8D mode. Since there is no register at the
* next location, just initialize the value to 0 and let the
* transaction go on.
*/
buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS;
buf[1] = 0;
}
op = (struct spi_mem_op)
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1),
SPI_MEM_OP_ADDR(enable ? 3 : 4,
SPINOR_REG_CYPRESS_CFR5V,
1),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(enable ? 1 : 2, buf, 1));
if (!enable)
spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR);
ret = spi_mem_exec_op(nor->spimem, &op);
if (ret)
return ret;
/* Read flash ID to make sure the switch was successful. */
op = (struct spi_mem_op)
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1),
SPI_MEM_OP_ADDR(enable ? 4 : 0, 0, 1),
SPI_MEM_OP_DUMMY(enable ? 3 : 0, 1),
SPI_MEM_OP_DATA_IN(round_up(nor->info->id_len, 2),
buf, 1));
if (enable)
spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR);
ret = spi_mem_exec_op(nor->spimem, &op);
if (ret)
return ret;
if (memcmp(buf, nor->info->id, nor->info->id_len))
return -EINVAL;
return 0;
return enable ? cypress_nor_octal_dtr_en(nor) :
cypress_nor_octal_dtr_dis(nor);
}
static void s28hs512t_default_init(struct spi_nor *nor)
@ -162,12 +173,12 @@ static int s28hs512t_post_bfpt_fixup(struct spi_nor *nor,
* CFR3V[4] and set the correct size.
*/
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 1),
SPI_MEM_OP_ADDR(3, SPINOR_REG_CYPRESS_CFR3V, 1),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1));
CYPRESS_NOR_RD_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR3V,
nor->bouncebuf);
int ret;
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
ret = spi_mem_exec_op(nor->spimem, &op);
if (ret)
return ret;
@ -317,11 +328,7 @@ static void spansion_nor_clear_sr(struct spi_nor *nor)
int ret;
if (nor->spimem) {
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 0),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_NO_DATA);
struct spi_mem_op op = SPANSION_CLSR_OP;
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);

View file

@ -8,6 +8,15 @@
#include "core.h"
#define WINBOND_NOR_OP_RDEAR 0xc8 /* Read Extended Address Register */
#define WINBOND_NOR_OP_WREAR 0xc5 /* Write Extended Address Register */
#define WINBOND_NOR_WREAR_OP(buf) \
SPI_MEM_OP(SPI_MEM_OP_CMD(WINBOND_NOR_OP_WREAR, 0), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(1, buf, 0))
static int
w25q256_post_bfpt_fixups(struct spi_nor *nor,
const struct sfdp_parameter_header *bfpt_header,
@ -124,11 +133,45 @@ static const struct flash_info winbond_nor_parts[] = {
{ "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ |
SPI_NOR_DUAL_READ) },
{ "w25q512nwm", INFO(0xef8020, 0, 64 * 1024, 1024)
PARSE_SFDP
OTP_INFO(256, 3, 0x1000, 0x1000) },
{ "w25q512jvq", INFO(0xef4020, 0, 64 * 1024, 1024)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
SPI_NOR_QUAD_READ) },
};
/**
* winbond_nor_write_ear() - Write Extended Address Register.
* @nor: pointer to 'struct spi_nor'.
* @ear: value to write to the Extended Address Register.
*
* Return: 0 on success, -errno otherwise.
*/
static int winbond_nor_write_ear(struct spi_nor *nor, u8 ear)
{
int ret;
nor->bouncebuf[0] = ear;
if (nor->spimem) {
struct spi_mem_op op = WINBOND_NOR_WREAR_OP(nor->bouncebuf);
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
ret = spi_mem_exec_op(nor->spimem, &op);
} else {
ret = spi_nor_controller_ops_write_reg(nor,
WINBOND_NOR_OP_WREAR,
nor->bouncebuf, 1);
}
if (ret)
dev_dbg(nor->dev, "error %d writing EAR\n", ret);
return ret;
}
/**
* winbond_nor_set_4byte_addr_mode() - Set 4-byte address mode for Winbond
* flashes.
@ -155,7 +198,7 @@ static int winbond_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
if (ret)
return ret;
ret = spi_nor_write_ear(nor, 0);
ret = winbond_nor_write_ear(nor, 0);
if (ret)
return ret;

View file

@ -15,6 +15,12 @@
#define XSR_PAGESIZE BIT(0) /* Page size in Po2 or Linear */
#define XSR_RDY BIT(7) /* Ready */
#define XILINX_RDSR_OP(buf) \
SPI_MEM_OP(SPI_MEM_OP_CMD(XILINX_OP_RDSR, 0), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_IN(1, buf, 0))
#define S3AN_INFO(_jedec_id, _n_sectors, _page_size) \
.id = { \
((_jedec_id) >> 16) & 0xff, \
@ -72,11 +78,7 @@ static int xilinx_nor_read_sr(struct spi_nor *nor, u8 *sr)
int ret;
if (nor->spimem) {
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(XILINX_OP_RDSR, 0),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(1, sr, 0));
struct spi_mem_op op = XILINX_RDSR_OP(sr);
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);

View file

@ -509,6 +509,7 @@ EXPORT_SYMBOL_GPL(of_platform_default_populate);
#ifndef CONFIG_PPC
static const struct of_device_id reserved_mem_matches[] = {
{ .compatible = "phram" },
{ .compatible = "qcom,rmtfs-mem" },
{ .compatible = "qcom,cmd-db" },
{ .compatible = "qcom,smem" },

View file

@ -286,6 +286,7 @@ struct cfi_private {
map_word sector_erase_cmd;
unsigned long chipshift; /* Because they're of the same type */
const char *im_name; /* inter_module name for cmdset_setup */
unsigned long quirks;
struct flchip chips[]; /* per-chip data structure for each chip */
};

View file

@ -47,8 +47,6 @@
#define SPINOR_OP_RDID 0x9f /* Read JEDEC ID */
#define SPINOR_OP_RDSFDP 0x5a /* Read SFDP */
#define SPINOR_OP_RDCR 0x35 /* Read configuration register */
#define SPINOR_OP_RDEAR 0xc8 /* Read Extended Address Register */
#define SPINOR_OP_WREAR 0xc5 /* Write Extended Address Register */
#define SPINOR_OP_SRSTEN 0x66 /* Software Reset Enable */
#define SPINOR_OP_SRST 0x99 /* Software Reset */
#define SPINOR_OP_GBULK 0x98 /* Global Block Unlock */
@ -365,6 +363,7 @@ struct spi_nor_flash_parameter;
* @write_proto: the SPI protocol for write operations
* @reg_proto: the SPI protocol for read_reg/write_reg/erase operations
* @sfdp: the SFDP data of the flash
* @debugfs_root: pointer to the debugfs directory
* @controller_ops: SPI NOR controller driver specific operations.
* @params: [FLASH-SPECIFIC] SPI NOR flash parameters and settings.
* The structure includes legacy flash parameters and
@ -394,6 +393,7 @@ struct spi_nor {
u32 flags;
enum spi_nor_cmd_ext cmd_ext_type;
struct sfdp *sfdp;
struct dentry *debugfs_root;
const struct spi_nor_controller_ops *controller_ops;

View file

@ -266,6 +266,7 @@ extern const struct spinand_manufacturer micron_spinand_manufacturer;
extern const struct spinand_manufacturer paragon_spinand_manufacturer;
extern const struct spinand_manufacturer toshiba_spinand_manufacturer;
extern const struct spinand_manufacturer winbond_spinand_manufacturer;
extern const struct spinand_manufacturer xtx_spinand_manufacturer;
/**
* struct spinand_op_variants - SPI NAND operation variants