diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-05-24 14:31:29 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-05-24 14:31:29 -0700 |
commit | d335371940179318df97d66baef13987ee6e796b (patch) | |
tree | aa7ee88e0b2f8c1eeaba662313ea37e58f9dc006 | |
parent | 076f222a690e11b433d2b1e218dbd9bdb08fb190 (diff) | |
parent | 2c51d0d88020b4d3c9bbe7d9df2796b2c2ce05b8 (diff) |
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
...
57 files changed, 1730 insertions, 820 deletions
diff --git a/Documentation/devicetree/bindings/mtd/elm.txt b/Documentation/devicetree/bindings/mtd/elm.txt deleted file mode 100644 index 59ddc61c1076..000000000000 --- a/Documentation/devicetree/bindings/mtd/elm.txt +++ /dev/null @@ -1,16 +0,0 @@ -Error location module - -Required properties: -- compatible: Must be "ti,am3352-elm" -- reg: physical base address and size of the registers map. -- interrupts: Interrupt number for the elm. - -Optional properties: -- ti,hwmods: Name of the hwmod associated to the elm - -Example: -elm: elm@0 { - compatible = "ti,am3352-elm"; - reg = <0x48080000 0x2000>; - interrupts = <4>; -}; diff --git a/Documentation/devicetree/bindings/mtd/jedec,spi-nor.yaml b/Documentation/devicetree/bindings/mtd/jedec,spi-nor.yaml index 4abfb4cfc157..7149784a36ac 100644 --- a/Documentation/devicetree/bindings/mtd/jedec,spi-nor.yaml +++ b/Documentation/devicetree/bindings/mtd/jedec,spi-nor.yaml @@ -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>; diff --git a/Documentation/devicetree/bindings/mtd/partitions/fixed-partitions.yaml b/Documentation/devicetree/bindings/mtd/partitions/fixed-partitions.yaml index ea4cace6a955..ad3ccd250802 100644 --- a/Documentation/devicetree/bindings/mtd/partitions/fixed-partitions.yaml +++ b/Documentation/devicetree/bindings/mtd/partitions/fixed-partitions.yaml @@ -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; + }; + }; diff --git a/Documentation/devicetree/bindings/mtd/renesas-nandc.yaml b/Documentation/devicetree/bindings/mtd/renesas-nandc.yaml index 2870d36361c4..7b18bc5cc8b3 100644 --- a/Documentation/devicetree/bindings/mtd/renesas-nandc.yaml +++ b/Documentation/devicetree/bindings/mtd/renesas-nandc.yaml @@ -36,11 +36,15 @@ properties: - const: hclk - const: eclk + power-domains: + maxItems: 1 + required: - compatible - reg - clocks - clock-names + - power-domains - interrupts unevaluatedProperties: false @@ -56,6 +60,7 @@ examples: interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>; clocks = <&sysctrl R9A06G032_HCLK_NAND>, <&sysctrl R9A06G032_CLK_NAND>; clock-names = "hclk", "eclk"; + power-domains = <&sysctrl>; #address-cells = <1>; #size-cells = <0>; }; diff --git a/Documentation/devicetree/bindings/mtd/ti,elm.yaml b/Documentation/devicetree/bindings/mtd/ti,elm.yaml new file mode 100644 index 000000000000..87128c004596 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/ti,elm.yaml @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mtd/ti,elm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments Error Location Module (ELM). + +maintainers: + - Roger Quadros <rogerq@kernel.org> + +description: + ELM module is used together with GPMC and NAND Flash to detect + errors and the location of the error based on BCH algorithms + so they can be corrected if possible. + +properties: + compatible: + enum: + - ti,am3352-elm + - ti,am64-elm + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + description: Functional clock. + + clock-names: + items: + - const: fck + + power-domains: + maxItems: 1 + + ti,hwmods: + description: + Name of the HWMOD associated with ELM. This is for legacy + platforms only. + $ref: /schemas/types.yaml#/definitions/string + deprecated: true + +required: + - compatible + - reg + - interrupts + +allOf: + - if: + properties: + compatible: + contains: + const: ti,am64-elm + then: + required: + - clocks + - clock-names + - power-domains + +additionalProperties: false + +examples: + - | + elm: ecc@0 { + compatible = "ti,am3352-elm"; + reg = <0x0 0x2000>; + interrupts = <4>; + }; diff --git a/Documentation/devicetree/bindings/reserved-memory/phram.yaml b/Documentation/devicetree/bindings/reserved-memory/phram.yaml new file mode 100644 index 000000000000..6c4db28015f1 --- /dev/null +++ b/Documentation/devicetree/bindings/reserved-memory/phram.yaml @@ -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>; + }; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 01430973ecec..65ff22364fb3 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -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,.*": diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index a761134fd3be..67453f59c69c 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -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[] = { @@ -802,46 +817,10 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd) } /* - * Return true if the chip is ready. - * - * Ready is one of: read mode, query mode, erase-suspend-read mode (in any - * non-suspended sector) and is indicated by no toggle bits toggling. - * - * Note that anything more complicated than checking if no bits are toggling - * (including checking DQ5 for an error status) is tricky to get working - * correctly and is therefore not done (particularly with interleaved chips - * as each chip must be checked independently of the others). - */ -static int __xipram chip_ready(struct map_info *map, struct flchip *chip, - unsigned long addr) -{ - struct cfi_private *cfi = map->fldrv_priv; - map_word d, t; - - if (cfi_use_status_reg(cfi)) { - map_word ready = CMD(CFI_SR_DRB); - /* - * For chips that support status register, check device - * ready bit - */ - cfi_send_gen_cmd(0x70, cfi->addr_unlock1, chip->start, map, cfi, - cfi->device_type, NULL); - d = map_read(map, addr); - - return map_word_andequal(map, d, ready, ready); - } - - d = map_read(map, addr); - t = map_read(map, addr); - - return map_word_equal(map, d, t); -} - -/* * Return true if the chip is ready and has the correct value. * * Ready is one of: read mode, query mode, erase-suspend-read mode (in any - * non-suspended sector) and it is indicated by no bits toggling. + * non-suspended sector) and is indicated by no toggle bits toggling. * * Error are indicated by toggling bits or bits held with the wrong value, * or with bits toggling. @@ -850,17 +829,16 @@ static int __xipram chip_ready(struct map_info *map, struct flchip *chip, * (including checking DQ5 for an error status) is tricky to get working * correctly and is therefore not done (particularly with interleaved chips * as each chip must be checked independently of the others). - * */ -static int __xipram chip_good(struct map_info *map, struct flchip *chip, - unsigned long addr, map_word expected) +static int __xipram chip_ready(struct map_info *map, struct flchip *chip, + unsigned long addr, map_word *expected) { struct cfi_private *cfi = map->fldrv_priv; map_word oldd, curd; + int ret; if (cfi_use_status_reg(cfi)) { map_word ready = CMD(CFI_SR_DRB); - /* * For chips that support status register, check device * ready bit @@ -875,8 +853,24 @@ static int __xipram chip_good(struct map_info *map, struct flchip *chip, oldd = map_read(map, addr); curd = map_read(map, addr); - return map_word_equal(map, oldd, curd) && - map_word_equal(map, curd, expected); + ret = map_word_equal(map, oldd, curd); + + if (!ret || !expected) + return ret; + + return map_word_equal(map, curd, *expected); +} + +static int __xipram chip_good(struct map_info *map, struct flchip *chip, + unsigned long addr, map_word *expected) +{ + struct cfi_private *cfi = map->fldrv_priv; + map_word *datum = expected; + + if (cfi->quirks & CFI_QUIRK_DQ_TRUE_DATA) + datum = NULL; + + return chip_ready(map, chip, addr, datum); } static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode) @@ -893,7 +887,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr case FL_STATUS: for (;;) { - if (chip_ready(map, chip, adr)) + if (chip_ready(map, chip, adr, NULL)) break; if (time_after(jiffies, timeo)) { @@ -932,7 +926,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr chip->state = FL_ERASE_SUSPENDING; chip->erase_suspended = 1; for (;;) { - if (chip_ready(map, chip, adr)) + if (chip_ready(map, chip, adr, NULL)) break; if (time_after(jiffies, timeo)) { @@ -1463,7 +1457,7 @@ static int do_otp_lock(struct map_info *map, struct flchip *chip, loff_t adr, /* wait for chip to become ready */ timeo = jiffies + msecs_to_jiffies(2); for (;;) { - if (chip_ready(map, chip, adr)) + if (chip_ready(map, chip, adr, NULL)) break; if (time_after(jiffies, timeo)) { @@ -1699,7 +1693,7 @@ static int __xipram do_write_oneword_once(struct map_info *map, * "chip_good" to avoid the failure due to scheduling. */ if (time_after(jiffies, timeo) && - !chip_good(map, chip, adr, datum)) { + !chip_good(map, chip, adr, &datum)) { xip_enable(map, chip, adr); printk(KERN_WARNING "MTD %s(): software timeout\n", __func__); xip_disable(map, chip, adr); @@ -1707,7 +1701,7 @@ static int __xipram do_write_oneword_once(struct map_info *map, break; } - if (chip_good(map, chip, adr, datum)) { + if (chip_good(map, chip, adr, &datum)) { if (cfi_check_err_status(map, chip, adr)) ret = -EIO; break; @@ -1979,14 +1973,14 @@ static int __xipram do_write_buffer_wait(struct map_info *map, * "chip_good" to avoid the failure due to scheduling. */ if (time_after(jiffies, timeo) && - !chip_good(map, chip, adr, datum)) { + !chip_good(map, chip, adr, &datum)) { pr_err("MTD %s(): software timeout, address:0x%.8lx.\n", __func__, adr); ret = -EIO; break; } - if (chip_good(map, chip, adr, datum)) { + if (chip_good(map, chip, adr, &datum)) { if (cfi_check_err_status(map, chip, adr)) ret = -EIO; break; @@ -2195,7 +2189,7 @@ static int cfi_amdstd_panic_wait(struct map_info *map, struct flchip *chip, * If the driver thinks the chip is idle, and no toggle bits * are changing, then the chip is actually idle for sure. */ - if (chip->state == FL_READY && chip_ready(map, chip, adr)) + if (chip->state == FL_READY && chip_ready(map, chip, adr, NULL)) return 0; /* @@ -2212,7 +2206,7 @@ static int cfi_amdstd_panic_wait(struct map_info *map, struct flchip *chip, /* wait for the chip to become ready */ for (i = 0; i < jiffies_to_usecs(timeo); i++) { - if (chip_ready(map, chip, adr)) + if (chip_ready(map, chip, adr, NULL)) return 0; udelay(1); @@ -2276,13 +2270,13 @@ retry: map_write(map, datum, adr); for (i = 0; i < jiffies_to_usecs(uWriteTimeout); i++) { - if (chip_ready(map, chip, adr)) + if (chip_ready(map, chip, adr, NULL)) break; udelay(1); } - if (!chip_good(map, chip, adr, datum) || + if (!chip_ready(map, chip, adr, &datum) || cfi_check_err_status(map, chip, adr)) { /* reset on all failures. */ map_write(map, CMD(0xF0), chip->start); @@ -2424,6 +2418,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) DECLARE_WAITQUEUE(wait, current); int ret; int retry_cnt = 0; + map_word datum = map_word_ff(map); adr = cfi->addr_unlock1; @@ -2478,7 +2473,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) chip->erase_suspended = 0; } - if (chip_good(map, chip, adr, map_word_ff(map))) { + if (chip_ready(map, chip, adr, &datum)) { if (cfi_check_err_status(map, chip, adr)) ret = -EIO; break; @@ -2523,6 +2518,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, DECLARE_WAITQUEUE(wait, current); int ret; int retry_cnt = 0; + map_word datum = map_word_ff(map); adr += chip->start; @@ -2577,7 +2573,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, chip->erase_suspended = 0; } - if (chip_good(map, chip, adr, map_word_ff(map))) { + if (chip_ready(map, chip, adr, &datum)) { if (cfi_check_err_status(map, chip, adr)) ret = -EIO; break; @@ -2771,7 +2767,7 @@ static int __maybe_unused do_ppb_xxlock(struct map_info *map, */ timeo = jiffies + msecs_to_jiffies(2000); /* 2s max (un)locking */ for (;;) { - if (chip_ready(map, chip, adr)) + if (chip_ready(map, chip, adr, NULL)) break; if (time_after(jiffies, timeo)) { diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index d503821a3e60..208bd4d871f4 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -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); diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 983999c020d6..d3377b10fc0f 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -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); } diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 6a099bbcd8be..e098ae937ce8 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -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 diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 2240b100f66a..094cfb244086 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -39,7 +39,6 @@ obj-$(CONFIG_MTD_IMPA7) += impa7.o obj-$(CONFIG_MTD_UCLINUX) += uclinux.o obj-$(CONFIG_MTD_NETtel) += nettel.o obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o -obj-$(CONFIG_MTD_IXP4XX) += ixp4xx.o obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o obj-$(CONFIG_MTD_VMU) += vmu-flash.o diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c deleted file mode 100644 index d8543201ab94..000000000000 --- a/drivers/mtd/maps/ixp4xx.c +++ /dev/null @@ -1,262 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * drivers/mtd/maps/ixp4xx.c - * - * MTD Map file for IXP4XX based systems. Please do not make per-board - * changes in here. If your board needs special setup, do it in your - * platform level code in arch/arm/mach-ixp4xx/board-setup.c - * - * Original Author: Intel Corporation - * Maintainer: Deepak Saxena <dsaxena@mvista.com> - * - * Copyright (C) 2002 Intel Corporation - * Copyright (C) 2003-2004 MontaVista Software, Inc. - * - */ - -#include <linux/err.h> -#include <linux/module.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/slab.h> -#include <linux/ioport.h> -#include <linux/device.h> -#include <linux/platform_device.h> - -#include <linux/mtd/mtd.h> -#include <linux/mtd/map.h> -#include <linux/mtd/partitions.h> - -#include <asm/io.h> -#include <asm/mach/flash.h> - -#include <linux/reboot.h> - -/* - * Read/write a 16 bit word from flash address 'addr'. - * - * When the cpu is in little-endian mode it swizzles the address lines - * ('address coherency') so we need to undo the swizzling to ensure commands - * and the like end up on the correct flash address. - * - * To further complicate matters, due to the way the expansion bus controller - * handles 32 bit reads, the byte stream ABCD is stored on the flash as: - * D15 D0 - * +---+---+ - * | A | B | 0 - * +---+---+ - * | C | D | 2 - * +---+---+ - * This means that on LE systems each 16 bit word must be swapped. Note that - * this requires CONFIG_MTD_CFI_BE_BYTE_SWAP to be enabled to 'unswap' the CFI - * data and other flash commands which are always in D7-D0. - */ -#ifndef __ARMEB__ -#ifndef CONFIG_MTD_CFI_BE_BYTE_SWAP -# error CONFIG_MTD_CFI_BE_BYTE_SWAP required -#endif - -static inline u16 flash_read16(void __iomem *addr) -{ - return be16_to_cpu(__raw_readw((void __iomem *)((unsigned long)addr ^ 0x2))); -} - -static inline void flash_write16(u16 d, void __iomem *addr) -{ - __raw_writew(cpu_to_be16(d), (void __iomem *)((unsigned long)addr ^ 0x2)); -} - -#define BYTE0(h) ((h) & 0xFF) -#define BYTE1(h) (((h) >> 8) & 0xFF) - -#else - -static inline u16 flash_read16(const void __iomem *addr) -{ - return __raw_readw(addr); -} - -static inline void flash_write16(u16 d, void __iomem *addr) -{ - __raw_writew(d, addr); -} - -#define BYTE0(h) (((h) >> 8) & 0xFF) -#define BYTE1(h) ((h) & 0xFF) -#endif - -static map_word ixp4xx_read16(struct map_info *map, unsigned long ofs) -{ - map_word val; - val.x[0] = flash_read16(map->virt + ofs); - return val; -} - -/* - * The IXP4xx expansion bus only allows 16-bit wide acceses - * when attached to a 16-bit wide device (such as the 28F128J3A), - * so we can't just memcpy_fromio(). - */ -static void ixp4xx_copy_from(struct map_info *map, void *to, - unsigned long from, ssize_t len) -{ - u8 *dest = (u8 *) to; - void __iomem *src = map->virt + from; - - if (len <= 0) - return; - - if (from & 1) { - *dest++ = BYTE1(flash_read16(src-1)); - src++; - --len; - } - - while (len >= 2) { - u16 data = flash_read16(src); - *dest++ = BYTE0(data); - *dest++ = BYTE1(data); - src += 2; - len -= 2; - } - - if (len > 0) - *dest++ = BYTE0(flash_read16(src)); -} - -/* - * Unaligned writes are ignored, causing the 8-bit - * probe to fail and proceed to the 16-bit probe (which succeeds). - */ -static void ixp4xx_probe_write16(struct map_info *map, map_word d, unsigned long adr) -{ - if (!(adr & 1)) - flash_write16(d.x[0], map->virt + adr); -} - -/* - * Fast write16 function without the probing check above - */ -static void ixp4xx_write16(struct map_info *map, map_word d, unsigned long adr) -{ - flash_write16(d.x[0], map->virt + adr); -} - -struct ixp4xx_flash_info { - struct mtd_info *mtd; - struct map_info map; - struct resource *res; -}; - -static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL }; - -static int ixp4xx_flash_remove(struct platform_device *dev) -{ - struct flash_platform_data *plat = dev_get_platdata(&dev->dev); - struct ixp4xx_flash_info *info = platform_get_drvdata(dev); - - if(!info) - return 0; - - if (info->mtd) { - mtd_device_unregister(info->mtd); - map_destroy(info->mtd); - } - - if (plat->exit) - plat->exit(); - - return 0; -} - -static int ixp4xx_flash_probe(struct platform_device *dev) -{ - struct flash_platform_data *plat = dev_get_platdata(&dev->dev); - struct ixp4xx_flash_info *info; - struct mtd_part_parser_data ppdata = { - .origin = dev->resource->start, - }; - int err = -1; - - if (!plat) - return -ENODEV; - - if (plat->init) { - err = plat->init(); - if (err) - return err; - } - - info = devm_kzalloc(&dev->dev, sizeof(struct ixp4xx_flash_info), - GFP_KERNEL); - if(!info) { - err = -ENOMEM; - goto Error; - } - - platform_set_drvdata(dev, info); - - /* - * Tell the MTD layer we're not 1:1 mapped so that it does - * not attempt to do a direct access on us. - */ - info->map.phys = NO_XIP; - info->map.size = resource_size(dev->resource); - - /* - * We only support 16-bit accesses for now. If and when - * any board use 8-bit access, we'll fixup the driver to - * handle that. - */ - info->map.bankwidth = 2; - info->map.name = dev_name(&dev->dev); - info->map.read = ixp4xx_read16; - info->map.write = ixp4xx_probe_write16; - info->map.copy_from = ixp4xx_copy_from; - - info->map.virt = devm_ioremap_resource(&dev->dev, dev->resource); - if (IS_ERR(info->map.virt)) { - err = PTR_ERR(info->map.virt); - goto Error; - } - - info->mtd = do_map_probe(plat->map_name, &info->map); - if (!info->mtd) { - printk(KERN_ERR "IXP4XXFlash: map_probe failed\n"); - err = -ENXIO; - goto Error; - } - info->mtd->dev.parent = &dev->dev; - - /* Use the fast version */ - info->map.write = ixp4xx_write16; - - err = mtd_device_parse_register(info->mtd, probes, &ppdata, - plat->parts, plat->nr_parts); - if (err) { - printk(KERN_ERR "Could not parse partitions\n"); - goto Error; - } - - return 0; - -Error: - ixp4xx_flash_remove(dev); - return err; -} - -static struct platform_driver ixp4xx_flash_driver = { - .probe = ixp4xx_flash_probe, - .remove = ixp4xx_flash_remove, - .driver = { - .name = "IXP4XX-Flash", - }, -}; - -module_platform_driver(ixp4xx_flash_driver); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("MTD map driver for Intel IXP4xx systems"); -MODULE_AUTHOR("Deepak Saxena"); -MODULE_ALIAS("platform:IXP4XX-Flash"); diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 03e3de3a5d79..1e94e7d10b8b 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -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); } diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 7731796024e0..9eb0680db312 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -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; diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index 227df24387df..3d4a2ffb5b01 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -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) { diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 357661b62c94..d442fa94c872 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -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); /* diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 9b249826ef93..5b0c2c95f10c 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -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 diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index a4e6b7ae0614..19e1291ac4d5 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -2,6 +2,7 @@ nandcore-objs := core.o bbt.o obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o +obj-$(CONFIG_MTD_NAND_ECC_MEDIATEK) += ecc-mtk.o obj-y += onenand/ obj-y += raw/ diff --git a/drivers/mtd/nand/raw/mtk_ecc.c b/drivers/mtd/nand/ecc-mtk.c index 49ab3448b9b1..9f9b201fe706 100644 --- a/drivers/mtd/nand/raw/mtk_ecc.c +++ b/drivers/mtd/nand/ecc-mtk.c @@ -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); diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 9b078e78f3fa..8b6d7a515445 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -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 diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index 88a566513c56..fa1d00120310 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -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 diff --git a/drivers/mtd/nand/raw/cadence-nand-controller.c b/drivers/mtd/nand/raw/cadence-nand-controller.c index 7eec60ea9056..0d72672f8b64 100644 --- a/drivers/mtd/nand/raw/cadence-nand-controller.c +++ b/drivers/mtd/nand/raw/cadence-nand-controller.c @@ -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)) diff --git a/drivers/mtd/nand/raw/cs553x_nand.c b/drivers/mtd/nand/raw/cs553x_nand.c index 6edf78c16fc8..f0a15717cf05 100644 --- a/drivers/mtd/nand/raw/cs553x_nand.c +++ b/drivers/mtd/nand/raw/cs553x_nand.c @@ -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, diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c index 45fec8c192ab..3e98e3c255bf 100644 --- a/drivers/mtd/nand/raw/davinci_nand.c +++ b/drivers/mtd/nand/raw/davinci_nand.c @@ -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); diff --git a/drivers/mtd/nand/raw/denali_pci.c b/drivers/mtd/nand/raw/denali_pci.c index 20c085a30adc..de7e722d3826 100644 --- a/drivers/mtd/nand/raw/denali_pci.c +++ b/drivers/mtd/nand/raw/denali_pci.c @@ -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 = { diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 44b14c9dc9a7..0b68d05846e1 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -218,7 +218,8 @@ static void gpmi_dump_info(struct gpmi_nand_data *this) "ECC Strength : %u\n" "Page Size in Bytes : %u\n" "Metadata Size in Bytes : %u\n" - "ECC Chunk Size in Bytes: %u\n" + "ECC0 Chunk Size in Bytes: %u\n" + "ECCn Chunk Size in Bytes: %u\n" "ECC Chunk Count : %u\n" "Payload Size in Bytes : %u\n" "Auxiliary Size in Bytes: %u\n" @@ -229,7 +230,8 @@ static void gpmi_dump_info(struct gpmi_nand_data *this) geo->ecc_strength, geo->page_size, geo->metadata_size, - geo->ecc_chunk_size, + geo->ecc0_chunk_size, + geo->eccn_chunk_size, geo->ecc_chunk_count, geo->payload_size, geo->auxiliary_size, @@ -238,9 +240,15 @@ static void gpmi_dump_info(struct gpmi_nand_data *this) geo->block_mark_bit_offset); } -static inline bool gpmi_check_ecc(struct gpmi_nand_data *this) +static bool gpmi_check_ecc(struct gpmi_nand_data *this) { + struct nand_chip *chip = &this->nand; struct bch_geometry *geo = &this->bch_geometry; + struct nand_device *nand = &chip->base; + struct nand_ecc_props *conf = &nand->ecc.ctx.conf; + + conf->step_size = geo->eccn_chunk_size; + conf->strength = geo->ecc_strength; /* Do the sanity check. */ if (GPMI_IS_MXS(this)) { @@ -248,7 +256,47 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this) if (geo->gf_len == 14) return false; } - return geo->ecc_strength <= this->devdata->bch_max_ecc_strength; + + if (geo->ecc_strength > this->devdata->bch_max_ecc_strength) + return false; + + if (!nand_ecc_is_strong_enough(nand)) + return false; + + return true; +} + +/* check if bbm locates in data chunk rather than ecc chunk */ +static bool bbm_in_data_chunk(struct gpmi_nand_data *this, + unsigned int *chunk_num) +{ + struct bch_geometry *geo = &this->bch_geometry; + struct nand_chip *chip = &this->nand; + struct mtd_info *mtd = nand_to_mtd(chip); + unsigned int i, j; + + if (geo->ecc0_chunk_size != geo->eccn_chunk_size) { + dev_err(this->dev, + "The size of ecc0_chunk must equal to eccn_chunk\n"); + return false; + } + + i = (mtd->writesize * 8 - geo->metadata_size * 8) / + (geo->gf_len * geo->ecc_strength + + geo->eccn_chunk_size * 8); + + j = (mtd->writesize * 8 - geo->metadata_size * 8) - + (geo->gf_len * geo->ecc_strength + + geo->eccn_chunk_size * 8) * i; + + if (j < geo->eccn_chunk_size * 8) { + *chunk_num = i+1; + dev_dbg(this->dev, "Set ecc to %d and bbm in chunk %d\n", + geo->ecc_strength, *chunk_num); + return true; + } + + return false; } /* @@ -280,13 +328,14 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this, nanddev_get_ecc_requirements(&chip->base)->step_size); return -EINVAL; } - geo->ecc_chunk_size = ecc_step; + geo->ecc0_chunk_size = ecc_step; + geo->eccn_chunk_size = ecc_step; geo->ecc_strength = round_up(ecc_strength, 2); if (!gpmi_check_ecc(this)) return -EINVAL; /* Keep the C >= O */ - if (geo->ecc_chunk_size < mtd->oobsize) { + if (geo->eccn_chunk_size < mtd->oobsize) { dev_err(this->dev, "unsupported nand chip. ecc size: %d, oob size : %d\n", ecc_step, mtd->oobsize); @@ -296,7 +345,7 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this, /* The default value, see comment in the legacy_set_geometry(). */ geo->metadata_size = 10; - geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; + geo->ecc_chunk_count = mtd->writesize / geo->eccn_chunk_size; /* * Now, the NAND chip with 2K page(data chunk is 512byte) shows below: @@ -399,6 +448,134 @@ static inline int get_ecc_strength(struct gpmi_nand_data *this) return round_down(ecc_strength, 2); } +static int set_geometry_for_large_oob(struct gpmi_nand_data *this) +{ + struct bch_geometry *geo = &this->bch_geometry; + struct nand_chip *chip = &this->nand; + struct mtd_info *mtd = nand_to_mtd(chip); + const struct nand_ecc_props *requirements = + nanddev_get_ecc_requirements(&chip->base); + unsigned int block_mark_bit_offset; + unsigned int max_ecc; + unsigned int bbm_chunk; + unsigned int i; + + /* sanity check for the minimum ecc nand required */ + if (!(requirements->strength > 0 && + requirements->step_size > 0)) + return -EINVAL; + geo->ecc_strength = requirements->strength; + + /* check if platform can support this nand */ + if (!gpmi_check_ecc(this)) { + dev_err(this->dev, + "unsupported NAND chip, minimum ecc required %d\n", + geo->ecc_strength); + return -EINVAL; + } + + /* calculate the maximum ecc platform can support*/ + geo->metadata_size = 10; + geo->gf_len = 14; + geo->ecc0_chunk_size = 1024; + geo->eccn_chunk_size = 1024; + geo->ecc_chunk_count = mtd->writesize / geo->eccn_chunk_size; + max_ecc = min(get_ecc_strength(this), + this->devdata->bch_max_ecc_strength); + + /* + * search a supported ecc strength that makes bbm + * located in data chunk + */ + geo->ecc_strength = max_ecc; + while (!(geo->ecc_strength < requirements->strength)) { + if (bbm_in_data_chunk(this, &bbm_chunk)) + goto geo_setting; + geo->ecc_strength -= 2; + } + + /* if none of them works, keep using the minimum ecc */ + /* nand required but changing ecc page layout */ + geo->ecc_strength = requirements->strength; + /* add extra ecc for meta data */ + geo->ecc0_chunk_size = 0; + geo->ecc_chunk_count = (mtd->writesize / geo->eccn_chunk_size) + 1; + geo->ecc_for_meta = 1; + /* check if oob can afford this extra ecc chunk */ + if (mtd->oobsize * 8 < geo->metadata_size * 8 + + geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) { + dev_err(this->dev, "unsupported NAND chip with new layout\n"); + return -EINVAL; + } + + /* calculate in which chunk bbm located */ + bbm_chunk = (mtd->writesize * 8 - geo->metadata_size * 8 - + geo->gf_len * geo->ecc_strength) / + (geo->gf_len * geo->ecc_strength + + geo->eccn_chunk_size * 8) + 1; + +geo_setting: + + geo->page_size = mtd->writesize + geo->metadata_size + + (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8; + geo->payload_size = mtd->writesize; + + /* + * The auxiliary buffer contains the metadata and the ECC status. The + * metadata is padded to the nearest 32-bit boundary. The ECC status + * contains one byte for every ECC chunk, and is also padded to the + * nearest 32-bit boundary. + */ + geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4); + geo->auxiliary_size = ALIGN(geo->metadata_size, 4) + + ALIGN(geo->ecc_chunk_count, 4); + + if (!this->swap_block_mark) + return 0; + + /* calculate the number of ecc chunk behind the bbm */ + i = (mtd->writesize / geo->eccn_chunk_size) - bbm_chunk + 1; + + block_mark_bit_offset = mtd->writesize * 8 - + (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - i) + + geo->metadata_size * 8); + + geo->block_mark_byte_offset = block_mark_bit_offset / 8; + geo->block_mark_bit_offset = block_mark_bit_offset % 8; + + dev_dbg(this->dev, "BCH Geometry :\n" + "GF length : %u\n" + "ECC Strength : %u\n" + "Page Size in Bytes : %u\n" + "Metadata Size in Bytes : %u\n" + "ECC0 Chunk Size in Bytes: %u\n" + "ECCn Chunk Size in Bytes: %u\n" + "ECC Chunk Count : %u\n" + "Payload Size in Bytes : %u\n" + "Auxiliary Size in Bytes: %u\n" + "Auxiliary Status Offset: %u\n" + "Block Mark Byte Offset : %u\n" + "Block Mark Bit Offset : %u\n" + "Block Mark in chunk : %u\n" + "Ecc for Meta data : %u\n", + geo->gf_len, + geo->ecc_strength, + geo->page_size, + geo->metadata_size, + geo->ecc0_chunk_size, + geo->eccn_chunk_size, + geo->ecc_chunk_count, + geo->payload_size, + geo->auxiliary_size, + geo->auxiliary_status_offset, + geo->block_mark_byte_offset, + geo->block_mark_bit_offset, + bbm_chunk, + geo->ecc_for_meta); + + return 0; +} + static int legacy_set_geometry(struct gpmi_nand_data *this) { struct bch_geometry *geo = &this->bch_geometry; @@ -418,13 +595,15 @@ static int legacy_set_geometry(struct gpmi_nand_data *this) geo->gf_len = 13; /* The default for chunk size. */ - geo->ecc_chunk_size = 512; - while (geo->ecc_chunk_size < mtd->oobsize) { - geo->ecc_chunk_size *= 2; /* keep C >= O */ + geo->ecc0_chunk_size = 512; + geo->eccn_chunk_size = 512; + while (geo->eccn_chunk_size < mtd->oobsize) { + geo->ecc0_chunk_size *= 2; /* keep C >= O */ + geo->eccn_chunk_size *= 2; /* keep C >= O */ geo->gf_len = 14; } - geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; + geo->ecc_chunk_count = mtd->writesize / geo->eccn_chunk_size; /* We use the same ECC strength for all chunks. */ geo->ecc_strength = get_ecc_strength(this); @@ -514,24 +693,40 @@ static int legacy_set_geometry(struct gpmi_nand_data *this) static int common_nfc_set_geometry(struct gpmi_nand_data *this) { struct nand_chip *chip = &this->nand; + struct mtd_info *mtd = nand_to_mtd(&this->nand); const struct nand_ecc_props *requirements = nanddev_get_ecc_requirements(&chip->base); + bool use_minimun_ecc; + int err; - if (chip->ecc.strength > 0 && chip->ecc.size > 0) - return set_geometry_by_ecc_info(this, chip->ecc.strength, - chip->ecc.size); + use_minimun_ecc = of_property_read_bool(this->dev->of_node, + "fsl,use-minimum-ecc"); - if ((of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc")) - || legacy_set_geometry(this)) { - if (!(requirements->strength > 0 && requirements->step_size > 0)) - return -EINVAL; + /* use legacy bch geometry settings by default*/ + if ((!use_minimun_ecc && mtd->oobsize < 1024) || + !(requirements->strength > 0 && requirements->step_size > 0)) { + dev_dbg(this->dev, "use legacy bch geometry\n"); + err = legacy_set_geometry(this); + if (!err) + return 0; + } - return set_geometry_by_ecc_info(this, - requirements->strength, - requirements->step_size); + /* for large oob nand */ + if (mtd->oobsize > 1024) { + dev_dbg(this->dev, "use large oob bch geometry\n"); + err = set_geometry_for_large_oob(this); + if (!err) + return 0; } - return 0; + /* otherwise use the minimum ecc nand chip required */ + dev_dbg(this->dev, "use minimum ecc bch geometry\n"); + err = set_geometry_by_ecc_info(this, requirements->strength, + requirements->step_size); + if (err) + dev_err(this->dev, "none of the bch geometry setting works\n"); + + return err; } /* Configures the geometry for BCH. */ @@ -843,7 +1038,7 @@ static int gpmi_raw_len_to_len(struct gpmi_nand_data *this, int raw_len) * we are passed in exec_op. Calculate the data length from it. */ if (this->bch) - return ALIGN_DOWN(raw_len, this->bch_geometry.ecc_chunk_size); + return ALIGN_DOWN(raw_len, this->bch_geometry.eccn_chunk_size); else return raw_len; } @@ -1235,7 +1430,7 @@ static int gpmi_count_bitflips(struct nand_chip *chip, void *buf, int first, /* Read ECC bytes into our internal raw_buffer */ offset = nfc_geo->metadata_size * 8; - offset += ((8 * nfc_geo->ecc_chunk_size) + eccbits) * (i + 1); + offset += ((8 * nfc_geo->eccn_chunk_size) + eccbits) * (i + 1); offset -= eccbits; bitoffset = offset % 8; eccbytes = DIV_ROUND_UP(offset + eccbits, 8); @@ -1272,16 +1467,16 @@ static int gpmi_count_bitflips(struct nand_chip *chip, void *buf, int first, if (i == 0) { /* The first block includes metadata */ flips = nand_check_erased_ecc_chunk( - buf + i * nfc_geo->ecc_chunk_size, - nfc_geo->ecc_chunk_size, + buf + i * nfc_geo->eccn_chunk_size, + nfc_geo->eccn_chunk_size, eccbuf, eccbytes, this->auxiliary_virt, nfc_geo->metadata_size, nfc_geo->ecc_strength); } else { flips = nand_check_erased_ecc_chunk( - buf + i * nfc_geo->ecc_chunk_size, - nfc_geo->ecc_chunk_size, + buf + i * nfc_geo->eccn_chunk_size, + nfc_geo->eccn_chunk_size, eccbuf, eccbytes, NULL, 0, nfc_geo->ecc_strength); @@ -1310,20 +1505,21 @@ static void gpmi_bch_layout_std(struct gpmi_nand_data *this) struct bch_geometry *geo = &this->bch_geometry; unsigned int ecc_strength = geo->ecc_strength >> 1; unsigned int gf_len = geo->gf_len; - unsigned int block_size = geo->ecc_chunk_size; + unsigned int block0_size = geo->ecc0_chunk_size; + unsigned int blockn_size = geo->eccn_chunk_size; this->bch_flashlayout0 = BF_BCH_FLASH0LAYOUT0_NBLOCKS(geo->ecc_chunk_count - 1) | BF_BCH_FLASH0LAYOUT0_META_SIZE(geo->metadata_size) | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) | BF_BCH_FLASH0LAYOUT0_GF(gf_len, this) | - BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size, this); + BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block0_size, this); this->bch_flashlayout1 = BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(geo->page_size) | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) | BF_BCH_FLASH0LAYOUT1_GF(gf_len, this) | - BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this); + BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(blockn_size, this); } static int gpmi_ecc_read_page(struct nand_chip *chip, uint8_t *buf, @@ -1406,29 +1602,49 @@ static int gpmi_ecc_read_subpage(struct nand_chip *chip, uint32_t offs, } } + /* + * if there is an ECC dedicate for meta: + * - need to add an extra ECC size when calculating col and page_size, + * if the meta size is NOT zero. + * - ecc0_chunk size need to set to the same size as other chunks, + * if the meta size is zero. + */ + meta = geo->metadata_size; if (first) { - col = meta + (size + ecc_parity_size) * first; + if (geo->ecc_for_meta) + col = meta + ecc_parity_size + + (size + ecc_parity_size) * first; + else + col = meta + (size + ecc_parity_size) * first; + meta = 0; buf = buf + first * size; } ecc_parity_size = geo->gf_len * geo->ecc_strength / 8; - n = last - first + 1; - page_size = meta + (size + ecc_parity_size) * n; + + if (geo->ecc_for_meta && meta) + page_size = meta + ecc_parity_size + + (size + ecc_parity_size) * n; + else + page_size = meta + (size + ecc_parity_size) * n; + ecc_strength = geo->ecc_strength >> 1; - this->bch_flashlayout0 = BF_BCH_FLASH0LAYOUT0_NBLOCKS(n - 1) | + this->bch_flashlayout0 = BF_BCH_FLASH0LAYOUT0_NBLOCKS( + (geo->ecc_for_meta ? n : n - 1)) | BF_BCH_FLASH0LAYOUT0_META_SIZE(meta) | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) | BF_BCH_FLASH0LAYOUT0_GF(geo->gf_len, this) | - BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(geo->ecc_chunk_size, this); + BF_BCH_FLASH0LAYOUT0_DATA0_SIZE((geo->ecc_for_meta ? + 0 : geo->ecc0_chunk_size), this); this->bch_flashlayout1 = BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) | BF_BCH_FLASH0LAYOUT1_GF(geo->gf_len, this) | - BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(geo->ecc_chunk_size, this); + BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(geo->eccn_chunk_size, this); this->bch = true; @@ -1597,7 +1813,7 @@ static int gpmi_ecc_read_page_raw(struct nand_chip *chip, uint8_t *buf, struct mtd_info *mtd = nand_to_mtd(chip); struct gpmi_nand_data *this = nand_get_controller_data(chip); struct bch_geometry *nfc_geo = &this->bch_geometry; - int eccsize = nfc_geo->ecc_chunk_size; + int eccsize = nfc_geo->eccn_chunk_size; int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len; u8 *tmp_buf = this->raw_buffer; size_t src_bit_off; @@ -1682,7 +1898,7 @@ static int gpmi_ecc_write_page_raw(struct nand_chip *chip, const uint8_t *buf, struct mtd_info *mtd = nand_to_mtd(chip); struct gpmi_nand_data *this = nand_get_controller_data(chip); struct bch_geometry *nfc_geo = &this->bch_geometry; - int eccsize = nfc_geo->ecc_chunk_size; + int eccsize = nfc_geo->eccn_chunk_size; int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len; u8 *tmp_buf = this->raw_buffer; uint8_t *oob = chip->oob_poi; @@ -2056,7 +2272,7 @@ static int gpmi_init_last(struct gpmi_nand_data *this) ecc->read_oob_raw = gpmi_ecc_read_oob_raw; ecc->write_oob_raw = gpmi_ecc_write_oob_raw; ecc->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; - ecc->size = bch_geo->ecc_chunk_size; + ecc->size = bch_geo->eccn_chunk_size; ecc->strength = bch_geo->ecc_strength; mtd_set_ooblayout(mtd, &gpmi_ooblayout_ops); diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h index 5e1c3ddae5f8..c3ff56ac62a7 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h @@ -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 */ }; /** diff --git a/drivers/mtd/nand/raw/intel-nand-controller.c b/drivers/mtd/nand/raw/intel-nand-controller.c index 7c1c80dae826..e91b879b32bd 100644 --- a/drivers/mtd/nand/raw/intel-nand-controller.c +++ b/drivers/mtd/nand/raw/intel-nand-controller.c @@ -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)) diff --git a/drivers/mtd/nand/raw/mpc5121_nfc.c b/drivers/mtd/nand/raw/mpc5121_nfc.c index 5b9271b9c326..800d774aed8e 100644 --- a/drivers/mtd/nand/raw/mpc5121_nfc.c +++ b/drivers/mtd/nand/raw/mpc5121_nfc.c @@ -595,8 +595,7 @@ static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd) struct nand_chip *chip = mtd_to_nand(mtd); struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); - if (prv->clk) - clk_disable_unprepare(prv->clk); + clk_disable_unprepare(prv->clk); if (prv->csreg) iounmap(prv->csreg); diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c index 66f04c693c87..d540454cbbdf 100644 --- a/drivers/mtd/nand/raw/mtk_nand.c +++ b/drivers/mtd/nand/raw/mtk_nand.c @@ -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) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 284fff62ac49..6b67b7dfe7ce 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -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; } diff --git a/drivers/mtd/nand/raw/nand_ids.c b/drivers/mtd/nand/raw/nand_ids.c index 6e41902be35f..88c2440b47d8 100644 --- a/drivers/mtd/nand/raw/nand_ids.c +++ b/drivers/mtd/nand/raw/nand_ids.c @@ -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), diff --git a/drivers/mtd/nand/raw/nand_toshiba.c b/drivers/mtd/nand/raw/nand_toshiba.c index cf4f37959421..d3d34d71921f 100644 --- a/drivers/mtd/nand/raw/nand_toshiba.c +++ b/drivers/mtd/nand/raw/nand_toshiba.c @@ -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; diff --git a/drivers/mtd/nand/raw/omap_elm.c b/drivers/mtd/nand/raw/omap_elm.c index 893e9979c4a2..4796a48e1012 100644 --- a/drivers/mtd/nand/raw/omap_elm.c +++ b/drivers/mtd/nand/raw/omap_elm.c @@ -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); diff --git a/drivers/mtd/nand/raw/renesas-nand-controller.c b/drivers/mtd/nand/raw/renesas-nand-controller.c index 6db063b230a9..1620e25a1147 100644 --- a/drivers/mtd/nand/raw/renesas-nand-controller.c +++ b/drivers/mtd/nand/raw/renesas-nand-controller.c @@ -16,6 +16,7 @@ #include <linux/mtd/rawnand.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/slab.h> #define COMMAND_REG 0x00 @@ -216,8 +217,7 @@ struct rnandc { struct nand_controller controller; struct device *dev; void __iomem *regs; - struct clk *hclk; - struct clk *eclk; + unsigned long ext_clk_rate; unsigned long assigned_cs; struct list_head chips; struct nand_chip *selected_chip; @@ -891,7 +891,7 @@ static int rnandc_setup_interface(struct nand_chip *chip, int chipnr, { struct rnand_chip *rnand = to_rnand(chip); struct rnandc *rnandc = to_rnandc(chip->controller); - unsigned int period_ns = 1000000000 / clk_get_rate(rnandc->eclk); + unsigned int period_ns = 1000000000 / rnandc->ext_clk_rate; const struct nand_sdr_timings *sdr; unsigned int cyc, cle, ale, bef_dly, ca_to_data; @@ -1319,6 +1319,7 @@ cleanup_chips: static int rnandc_probe(struct platform_device *pdev) { struct rnandc *rnandc; + struct clk *eclk; int irq, ret; rnandc = devm_kzalloc(&pdev->dev, sizeof(*rnandc), GFP_KERNEL); @@ -1335,29 +1336,26 @@ static int rnandc_probe(struct platform_device *pdev) if (IS_ERR(rnandc->regs)) return PTR_ERR(rnandc->regs); - /* APB clock */ - rnandc->hclk = devm_clk_get(&pdev->dev, "hclk"); - if (IS_ERR(rnandc->hclk)) - return PTR_ERR(rnandc->hclk); - - /* External NAND bus clock */ - rnandc->eclk = devm_clk_get(&pdev->dev, "eclk"); - if (IS_ERR(rnandc->eclk)) - return PTR_ERR(rnandc->eclk); - - ret = clk_prepare_enable(rnandc->hclk); - if (ret) + devm_pm_runtime_enable(&pdev->dev); + ret = pm_runtime_resume_and_get(&pdev->dev); + if (ret < 0) return ret; - ret = clk_prepare_enable(rnandc->eclk); - if (ret) - goto disable_hclk; + /* The external NAND bus clock rate is needed for computing timings */ + eclk = clk_get(&pdev->dev, "eclk"); + if (IS_ERR(eclk)) { + ret = PTR_ERR(eclk); + goto dis_runtime_pm; + } + + rnandc->ext_clk_rate = clk_get_rate(eclk); + clk_put(eclk); rnandc_dis_interrupts(rnandc); irq = platform_get_irq_optional(pdev, 0); if (irq == -EPROBE_DEFER) { ret = irq; - goto disable_eclk; + goto dis_runtime_pm; } else if (irq < 0) { dev_info(&pdev->dev, "No IRQ found, fallback to polling\n"); rnandc->use_polling = true; @@ -1365,12 +1363,12 @@ static int rnandc_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, irq, rnandc_irq_handler, 0, "renesas-nand-controller", rnandc); if (ret < 0) - goto disable_eclk; + goto dis_runtime_pm; } ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); if (ret) - goto disable_eclk; + goto dis_runtime_pm; rnandc_clear_fifo(rnandc); @@ -1378,14 +1376,12 @@ static int rnandc_probe(struct platform_device *pdev) ret = rnandc_chips_init(rnandc); if (ret) - goto disable_eclk; + goto dis_runtime_pm; return 0; -disable_eclk: - clk_disable_unprepare(rnandc->eclk); -disable_hclk: - clk_disable_unprepare(rnandc->hclk); +dis_runtime_pm: + pm_runtime_put(&pdev->dev); return ret; } @@ -1396,8 +1392,7 @@ static int rnandc_remove(struct platform_device *pdev) rnandc_chips_cleanup(rnandc); - clk_disable_unprepare(rnandc->eclk); - clk_disable_unprepare(rnandc->hclk); + pm_runtime_put(&pdev->dev); return 0; } diff --git a/drivers/mtd/nand/raw/rockchip-nand-controller.c b/drivers/mtd/nand/raw/rockchip-nand-controller.c index cbaa4f1c83da..f133985cc053 100644 --- a/drivers/mtd/nand/raw/rockchip-nand-controller.c +++ b/drivers/mtd/nand/raw/rockchip-nand-controller.c @@ -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); } diff --git a/drivers/mtd/nand/raw/tmio_nand.c b/drivers/mtd/nand/raw/tmio_nand.c index de8e919d0ebe..8f1a42bf199c 100644 --- a/drivers/mtd/nand/raw/tmio_nand.c +++ b/drivers/mtd/nand/raw/tmio_nand.c @@ -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; diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index 9662b9c1d5a9..80dabe6ff0f3 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -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 diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index ff8336870bc0..d5b685d1605e 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -933,6 +933,7 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = { ¶gon_spinand_manufacturer, &toshiba_spinand_manufacturer, &winbond_spinand_manufacturer, + &xtx_spinand_manufacturer, }; static int spinand_manufacturer_match(struct spinand_device *spinand, diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c index 1dd1c5898093..6b043e24855f 100644 --- a/drivers/mtd/nand/spi/gigadevice.c +++ b/drivers/mtd/nand/spi/gigadevice.c @@ -39,6 +39,22 @@ static SPINAND_OP_VARIANTS(read_cache_variants_f, SPINAND_PAGE_READ_FROM_CACHE_OP_3A(true, 0, 1, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_OP_3A(false, 0, 0, NULL, 0)); +static SPINAND_OP_VARIANTS(read_cache_variants_1gq5, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +static SPINAND_OP_VARIANTS(read_cache_variants_2gq5, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 4, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 2, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + static SPINAND_OP_VARIANTS(write_cache_variants, SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), SPINAND_PROG_LOAD(true, 0, NULL, 0)); @@ -325,6 +341,36 @@ static const struct spinand_info gigadevice_spinand_table[] = { SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F1GQ4RExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xc1), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F2GQ4UExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xd2), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F2GQ4RExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xc2), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq4uexxg_ecc_get_status)), SPINAND_INFO("GD5F1GQ4UFxxG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), @@ -339,12 +385,122 @@ static const struct spinand_info gigadevice_spinand_table[] = { SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x51), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(4, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq5xexxg_ecc_get_status)), + SPINAND_INFO("GD5F1GQ5RExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x41), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5, &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, gd5fxgq5xexxg_ecc_get_status)), + SPINAND_INFO("GD5F2GQ5UExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x52), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq5xexxg_ecc_get_status)), + SPINAND_INFO("GD5F2GQ5RExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x42), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq5xexxg_ecc_get_status)), + SPINAND_INFO("GD5F4GQ6UExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x55), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 2, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq5xexxg_ecc_get_status)), + SPINAND_INFO("GD5F4GQ6RExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x45), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 2, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq5xexxg_ecc_get_status)), + SPINAND_INFO("GD5F1GM7UExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x91), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F1GM7RExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x81), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F2GM7UExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F2GM7RExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x82), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F4GM8UExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x95), + NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F4GM8RExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x85), + NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq4uexxg_ecc_get_status)), }; static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = { diff --git a/drivers/mtd/nand/spi/xtx.c b/drivers/mtd/nand/spi/xtx.c new file mode 100644 index 000000000000..3911520f718c --- /dev/null +++ b/drivers/mtd/nand/spi/xtx.c @@ -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, +}; diff --git a/drivers/mtd/parsers/bcm47xxpart.c b/drivers/mtd/parsers/bcm47xxpart.c index 6012a10f10c8..50fcf4c2174b 100644 --- a/drivers/mtd/parsers/bcm47xxpart.c +++ b/drivers/mtd/parsers/bcm47xxpart.c @@ -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; } diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 6b904e439372..e347b435a038 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -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/ diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index b4f141ad9c9c..502967c76c5f 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -308,6 +308,52 @@ ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len, } /** + * 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); @@ -370,6 +408,37 @@ int spi_nor_write_disable(struct spi_nor *nor) } /** + * 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'. * @sr: pointer to a DMA-able buffer where the value of the @@ -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); @@ -521,40 +571,6 @@ static int spansion_set_4byte_addr_mode(struct spi_nor *nor, bool enable) } /** - * 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. * @nor: pointer to 'struct spi_nor'. @@ -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 diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index b7fd760e3b47..3f841ec36e56 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -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 */ diff --git a/drivers/mtd/spi-nor/debugfs.c b/drivers/mtd/spi-nor/debugfs.c new file mode 100644 index 000000000000..eaf84f7a0676 --- /dev/null +++ b/drivers/mtd/spi-nor/debugfs.c @@ -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 = ¶ms->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), ¶ms->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, ¶ms->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); +} diff --git a/drivers/mtd/spi-nor/eon.c b/drivers/mtd/spi-nor/eon.c index 8c1c57530281..50a11053711f 100644 --- a/drivers/mtd/spi-nor/eon.c +++ b/drivers/mtd/spi-nor/eon.c @@ -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) }, }; diff --git a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c index 8a20475ce77a..a96f74e0f568 100644 --- a/drivers/mtd/spi-nor/micron-st.c +++ b/drivers/mtd/spi-nor/micron-st.c @@ -28,82 +28,87 @@ #define FSR_P_ERR BIT(4) /* Program operation status */ #define FSR_PT_ERR BIT(1) /* Protection error bit */ -static int micron_st_nor_octal_dtr_enable(struct spi_nor *nor, bool enable) +/* Micron ST SPI NOR flash operations. */ +#define MICRON_ST_NOR_WR_ANY_REG_OP(naddr, addr, ndata, buf) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 0), \ + SPI_MEM_OP_ADDR(naddr, addr, 0), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(ndata, buf, 0)) + +#define MICRON_ST_RDFSR_OP(buf) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 0), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_IN(1, buf, 0)) + +#define MICRON_ST_CLFSR_OP \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 0), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +static int micron_st_nor_octal_dtr_en(struct spi_nor *nor) { struct spi_mem_op op; u8 *buf = nor->bouncebuf; int ret; - if (enable) { - /* Use 20 dummy cycles for memory array reads. */ - ret = spi_nor_write_enable(nor); - if (ret) - return ret; - - *buf = 20; - op = (struct spi_mem_op) - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1), - SPI_MEM_OP_ADDR(3, SPINOR_REG_MT_CFR1V, 1), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(1, buf, 1)); - - ret = spi_mem_exec_op(nor->spimem, &op); - if (ret) - return ret; - - ret = spi_nor_wait_till_ready(nor); - if (ret) - return ret; - } + /* Use 20 dummy cycles for memory array reads. */ + *buf = 20; + op = (struct spi_mem_op) + MICRON_ST_NOR_WR_ANY_REG_OP(3, SPINOR_REG_MT_CFR1V, 1, buf); + ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto); + if (ret) + return ret; - ret = spi_nor_write_enable(nor); + buf[0] = SPINOR_MT_OCT_DTR; + op = (struct spi_mem_op) + MICRON_ST_NOR_WR_ANY_REG_OP(3, SPINOR_REG_MT_CFR0V, 1, buf); + ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto); if (ret) return ret; - if (enable) { - buf[0] = SPINOR_MT_OCT_DTR; - } else { - /* - * The register is 1-byte wide, but 1-byte transactions are not - * allowed in 8D-8D-8D mode. The next register is the dummy - * cycle configuration register. Since the transaction needs to - * be at least 2 bytes wide, set the next register to its - * default value. This also makes sense because the value was - * changed when enabling 8D-8D-8D mode, it should be reset when - * disabling. - */ - buf[0] = SPINOR_MT_EXSPI; - buf[1] = SPINOR_REG_MT_CFR1V_DEF; + /* Read flash ID to make sure the switch was successful. */ + ret = spi_nor_read_id(nor, 0, 8, buf, SNOR_PROTO_8_8_8_DTR); + if (ret) { + dev_dbg(nor->dev, "error %d reading JEDEC ID after enabling 8D-8D-8D mode\n", ret); + return ret; } - op = (struct spi_mem_op) - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1), - SPI_MEM_OP_ADDR(enable ? 3 : 4, - SPINOR_REG_MT_CFR0V, 1), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(enable ? 1 : 2, buf, 1)); + if (memcmp(buf, nor->info->id, nor->info->id_len)) + return -EINVAL; - if (!enable) - spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR); + return 0; +} - ret = spi_mem_exec_op(nor->spimem, &op); +static int micron_st_nor_octal_dtr_dis(struct spi_nor *nor) +{ + struct spi_mem_op op; + u8 *buf = nor->bouncebuf; + int ret; + + /* + * The register is 1-byte wide, but 1-byte transactions are not allowed + * in 8D-8D-8D mode. The next register is the dummy cycle configuration + * register. Since the transaction needs to be at least 2 bytes wide, + * set the next register to its default value. This also makes sense + * because the value was changed when enabling 8D-8D-8D mode, it should + * be reset when disabling. + */ + buf[0] = SPINOR_MT_EXSPI; + buf[1] = SPINOR_REG_MT_CFR1V_DEF; + op = (struct spi_mem_op) + MICRON_ST_NOR_WR_ANY_REG_OP(4, SPINOR_REG_MT_CFR0V, 2, buf); + ret = spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8_DTR); if (ret) return ret; /* Read flash ID to make sure the switch was successful. */ - op = (struct spi_mem_op) - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_DUMMY(enable ? 8 : 0, 1), - SPI_MEM_OP_DATA_IN(round_up(nor->info->id_len, 2), - buf, 1)); - - if (enable) - spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR); - - ret = spi_mem_exec_op(nor->spimem, &op); - if (ret) + ret = spi_nor_read_id(nor, 0, 0, buf, SNOR_PROTO_1_1_1); + if (ret) { + dev_dbg(nor->dev, "error %d reading JEDEC ID after disabling 8D-8D-8D mode\n", ret); return ret; + } if (memcmp(buf, nor->info->id, nor->info->id_len)) return -EINVAL; @@ -111,6 +116,12 @@ static int micron_st_nor_octal_dtr_enable(struct spi_nor *nor, bool enable) return 0; } +static int micron_st_nor_octal_dtr_enable(struct spi_nor *nor, bool enable) +{ + return enable ? micron_st_nor_octal_dtr_en(nor) : + micron_st_nor_octal_dtr_dis(nor); +} + static void mt35xu512aba_default_init(struct spi_nor *nor) { nor->params->octal_dtr_enable = micron_st_nor_octal_dtr_enable; @@ -322,11 +333,7 @@ static int micron_st_nor_read_fsr(struct spi_nor *nor, u8 *fsr) int ret; if (nor->spimem) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 0), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(1, fsr, 0)); + struct spi_mem_op op = MICRON_ST_RDFSR_OP(fsr); if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) { op.addr.nbytes = nor->params->rdsr_addr_nbytes; @@ -361,11 +368,7 @@ static void micron_st_nor_clear_fsr(struct spi_nor *nor) int ret; if (nor->spimem) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 0), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_NO_DATA); + struct spi_mem_op op = MICRON_ST_CLFSR_OP; spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index f24e546e04a5..43cd6cd92537 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -23,94 +23,89 @@ #define SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS 0 #define SPINOR_OP_CYPRESS_RD_FAST 0xee -/** - * cypress_nor_octal_dtr_enable() - Enable octal DTR on Cypress flashes. - * @nor: pointer to a 'struct spi_nor' - * @enable: whether to enable or disable Octal DTR - * - * This also sets the memory access latency cycles to 24 to allow the flash to - * run at up to 200MHz. - * - * Return: 0 on success, -errno otherwise. - */ -static int cypress_nor_octal_dtr_enable(struct spi_nor *nor, bool enable) +/* Cypress SPI NOR flash operations. */ +#define CYPRESS_NOR_WR_ANY_REG_OP(naddr, addr, ndata, buf) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 0), \ + SPI_MEM_OP_ADDR(naddr, addr, 0), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(ndata, buf, 0)) + +#define CYPRESS_NOR_RD_ANY_REG_OP(naddr, addr, buf) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 0), \ + SPI_MEM_OP_ADDR(naddr, addr, 0), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_IN(1, buf, 0)) + +#define SPANSION_CLSR_OP \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 0), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +static int cypress_nor_octal_dtr_en(struct spi_nor *nor) { struct spi_mem_op op; u8 *buf = nor->bouncebuf; int ret; - if (enable) { - /* Use 24 dummy cycles for memory array reads. */ - ret = spi_nor_write_enable(nor); - if (ret) - return ret; - - *buf = SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24; - op = (struct spi_mem_op) - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1), - SPI_MEM_OP_ADDR(3, SPINOR_REG_CYPRESS_CFR2V, - 1), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(1, buf, 1)); + /* Use 24 dummy cycles for memory array reads. */ + *buf = SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24; + op = (struct spi_mem_op) + CYPRESS_NOR_WR_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR2V, 1, buf); - ret = spi_mem_exec_op(nor->spimem, &op); - if (ret) - return ret; + ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto); + if (ret) + return ret; - ret = spi_nor_wait_till_ready(nor); - if (ret) - return ret; + nor->read_dummy = 24; - nor->read_dummy = 24; - } + /* Set the octal and DTR enable bits. */ + buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN; + op = (struct spi_mem_op) + CYPRESS_NOR_WR_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR5V, 1, buf); - /* Set/unset the octal and DTR enable bits. */ - ret = spi_nor_write_enable(nor); + ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto); if (ret) return ret; - if (enable) { - buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN; - } else { - /* - * The register is 1-byte wide, but 1-byte transactions are not - * allowed in 8D-8D-8D mode. Since there is no register at the - * next location, just initialize the value to 0 and let the - * transaction go on. - */ - buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS; - buf[1] = 0; + /* Read flash ID to make sure the switch was successful. */ + ret = spi_nor_read_id(nor, 4, 3, buf, SNOR_PROTO_8_8_8_DTR); + if (ret) { + dev_dbg(nor->dev, "error %d reading JEDEC ID after enabling 8D-8D-8D mode\n", ret); + return ret; } - op = (struct spi_mem_op) - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1), - SPI_MEM_OP_ADDR(enable ? 3 : 4, - SPINOR_REG_CYPRESS_CFR5V, - 1), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(enable ? 1 : 2, buf, 1)); + if (memcmp(buf, nor->info->id, nor->info->id_len)) + return -EINVAL; - if (!enable) - spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR); + return 0; +} - ret = spi_mem_exec_op(nor->spimem, &op); +static int cypress_nor_octal_dtr_dis(struct spi_nor *nor) +{ + struct spi_mem_op op; + u8 *buf = nor->bouncebuf; + int ret; + + /* + * The register is 1-byte wide, but 1-byte transactions are not allowed + * in 8D-8D-8D mode. Since there is no register at the next location, + * just initialize the value to 0 and let the transaction go on. + */ + buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS; + buf[1] = 0; + op = (struct spi_mem_op) + CYPRESS_NOR_WR_ANY_REG_OP(4, SPINOR_REG_CYPRESS_CFR5V, 2, buf); + ret = spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8_DTR); if (ret) return ret; /* Read flash ID to make sure the switch was successful. */ - op = (struct spi_mem_op) - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1), - SPI_MEM_OP_ADDR(enable ? 4 : 0, 0, 1), - SPI_MEM_OP_DUMMY(enable ? 3 : 0, 1), - SPI_MEM_OP_DATA_IN(round_up(nor->info->id_len, 2), - buf, 1)); - - if (enable) - spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR); - - ret = spi_mem_exec_op(nor->spimem, &op); - if (ret) + ret = spi_nor_read_id(nor, 0, 0, buf, SNOR_PROTO_1_1_1); + if (ret) { + dev_dbg(nor->dev, "error %d reading JEDEC ID after disabling 8D-8D-8D mode\n", ret); return ret; + } if (memcmp(buf, nor->info->id, nor->info->id_len)) return -EINVAL; @@ -118,6 +113,22 @@ static int cypress_nor_octal_dtr_enable(struct spi_nor *nor, bool enable) return 0; } +/** + * cypress_nor_octal_dtr_enable() - Enable octal DTR on Cypress flashes. + * @nor: pointer to a 'struct spi_nor' + * @enable: whether to enable or disable Octal DTR + * + * This also sets the memory access latency cycles to 24 to allow the flash to + * run at up to 200MHz. + * + * Return: 0 on success, -errno otherwise. + */ +static int cypress_nor_octal_dtr_enable(struct spi_nor *nor, bool enable) +{ + return enable ? cypress_nor_octal_dtr_en(nor) : + cypress_nor_octal_dtr_dis(nor); +} + static void s28hs512t_default_init(struct spi_nor *nor) { nor->params->octal_dtr_enable = cypress_nor_octal_dtr_enable; @@ -162,12 +173,12 @@ static int s28hs512t_post_bfpt_fixup(struct spi_nor *nor, * CFR3V[4] and set the correct size. */ struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 1), - SPI_MEM_OP_ADDR(3, SPINOR_REG_CYPRESS_CFR3V, 1), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1)); + CYPRESS_NOR_RD_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR3V, + nor->bouncebuf); int ret; + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + ret = spi_mem_exec_op(nor->spimem, &op); if (ret) return ret; @@ -317,11 +328,7 @@ static void spansion_nor_clear_sr(struct spi_nor *nor) int ret; if (nor->spimem) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 0), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_NO_DATA); + struct spi_mem_op op = SPANSION_CLSR_OP; spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index fe80dffc2e70..ffaa24055259 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -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,12 +133,46 @@ 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. * @nor: pointer to 'struct spi_nor'. @@ -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; diff --git a/drivers/mtd/spi-nor/xilinx.c b/drivers/mtd/spi-nor/xilinx.c index 9459ac2609dc..1d2f5db047bd 100644 --- a/drivers/mtd/spi-nor/xilinx.c +++ b/drivers/mtd/spi-nor/xilinx.c @@ -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); diff --git a/drivers/of/platform.c b/drivers/of/platform.c index a16b74f32aa9..55d62b82c650 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -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" }, diff --git a/include/linux/mtd/cfi.h b/include/linux/mtd/cfi.h index fd1ecb821106..d88bb56c18e2 100644 --- a/include/linux/mtd/cfi.h +++ b/include/linux/mtd/cfi.h @@ -286,6 +286,7 @@ struct cfi_private { map_word sector_erase_cmd; unsigned long chipshift; /* Because they're of the same type */ const char *im_name; /* inter_module name for cmdset_setup */ + unsigned long quirks; struct flchip chips[]; /* per-chip data structure for each chip */ }; diff --git a/drivers/mtd/nand/raw/mtk_ecc.h b/include/linux/mtd/nand-ecc-mtk.h index 0e48c36e6ca0..0e48c36e6ca0 100644 --- a/drivers/mtd/nand/raw/mtk_ecc.h +++ b/include/linux/mtd/nand-ecc-mtk.h diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 5e25a7b75ae2..1ede4c89805a 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -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; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 3aa28240a77f..5584d3bb6556 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -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 |