MTD core changes:
* Fix refcount error in del_mtd_device() * Fix possible resource leak in init_mtd() * Set ROOT_DEV for partitions marked as rootfs in DT * Describe marking rootfs partitions in the bindings * Fix device name leak when register device failed in add_mtd_device() * Try to find OF node for every MTD partition * simplify (a bit) code find partition-matching dynamic OF node MTD driver changes: * pxa2xx-flash maps: fix memory leak in probe * BCM parser: refer to ARCH_BCMBCA instead of ARCH_BCM4908 * lpddr2_nvm: Fix possible null-ptr-deref * inftlcore: fix repeated words in comments * lart: remove driver * tplink: - Add TP-Link SafeLoader partitions table parser and bindings - Describe TP-Link SafeLoader parser - Describe TP-Link SafeLoader dynamic subpartitions * mtdoops: - Panic caused mtdoops to call mtdoops_erase function immediately - Add mtdoops_erase function and move mtdoops_inc_counter to after it - Change printk() to counterpart pr_ functions MTD binding cleanup: * Fixed-partitions: Fix 'sercomm,scpart-id' schema * Standardize the style in the examples * Drop object types when referencing other files * Argue in favor of keeping additionalProperties set to true * NVMEM-cells: - Inherit from MTD partitions - Drop range property from example * Partitions: - Change qcom,smem-part partition type - Constrain the list of parsers * Physmap: Reuse the generic definitions * SPI-NOR: Drop common properties * Sunxi-nand: Add an example to validate the bindings * Onenand: Mention the expected node name * Ingenic: Mark partitions in the controller node as deprecated * NAND: - Standardize the child node name - Drop common properties already defined in generic files - nand-chip.yaml should reference mtd.yaml * Remove useless file about partitions * Clarify all partition subnodes SPI NOR core changes: * Add support for flash reset using the dt reset-gpios property. * Update hwcaps.mask to include 8D-8D-8D read and page program ops when xSPI profile 1.0 table is defined. * Bypass zero erase size in spi_nor_find_best_erase_type(). * Fix select_uniform_erase to skip 0 erase size * Add generic flash driver. If a flash is not found in the flash_info array, fall back to the generic flash driver which is described solely by the flash's SFDP tables. * Fix the number of bytes for the dummy cycles in spi_nor_spimem_check_readop(). * Introduce SPI_NOR_QUAD_PP flag, as PP_1_1_4 is not SFDP discoverable. SPI NOR manufacturer drivers changes: * Spansion: - use PARSE_SFDP for s28hs512t, - add support for s28hl512t, s28hl01gt, and s28hs01gt. * Gigadevice: Replace default_init() with post_bfpt() for gd25q256. * Micron - ST: Enable locking for mt25qu256a. * Winbond: Add support for W25Q512NW-IQ. * ISSI: Use PARSE_SFDP and SPI_NOR_QUAD_PP. Raw NAND core changes: * Drop obsolete dependencies on COMPILE_TEST * MAINTAINERS: rectify entry for MESON NAND controller bindings * Drop EXPORT_SYMBOL_GPL for nanddev_erase() Raw NAND driver changes: * marvell: Enable NFC/DEVBUS arbiter * gpmi: Use pm_runtime_resume_and_get instead of pm_runtime_get_sync * mpc5121: Replace NO_IRQ by 0 * lpc32xx_{slc,mlc}: - Switch to using pm_ptr() - Switch to using gpiod API * lpc32xx_mlc: Switch to using pm_ptr() * cadence: Support 64-bit slave dma interface * rockchip: Describe rk3128-nfc in the bindings * brcmnand: Update interrupts description in the bindings SPI-NAND driver changes: * winbond: - Add Winbond W25N02KV flash support - Fix flash identification -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEE9HuaYnbmDhq/XIDIJWrqGEe9VoQFAmOOBbQACgkQJWrqGEe9 VoRB7wf9EY8IeRhumaeN9WT+VrmPxGOu61m/eilUK4xvgj6JX/yhYo/kKW9eEp+o 5I8JLimG6uo7rZNlbixGMbHd41aTu9MplLrTA42nzGadl3w4D8p9c1ic6sEiv7SI qgnAbzq3FLUPDLLDV0IfrMQvsQgJkv3b+r8ShPt9uJy3cmnKQzOvIhs43wwLnOXp NPiMbVjpS4FAmNvG7nI0xfcBLO/eFaTuQc+UyfP2SwkVIAN5xfUAwOPOHZWBstmR zhJlHSFJWrg+J9T9bsRW7D5q/MVxqNsMH589c9aXHNqzgzZs2FTopDUf6cWwHr0f 2Ssot2naIdvU+eLGA12hDuUDDoRwKA== =Igti -----END PGP SIGNATURE----- Merge tag 'mtd/for-6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux Pull mtd updates from Miquel Raynal: "MTD core changes: - Fix refcount error in del_mtd_device() - Fix possible resource leak in init_mtd() - Set ROOT_DEV for partitions marked as rootfs in DT - Describe marking rootfs partitions in the bindings - Fix device name leak when register device fails in add_mtd_device() - Try to find OF node for every MTD partition - simplify (a bit) code find partition-matching dynamic OF node MTD driver changes: - pxa2xx-flash maps: fix memory leak in probe - BCM parser: refer to ARCH_BCMBCA instead of ARCH_BCM4908 - lpddr2_nvm: Fix possible null-ptr-deref - inftlcore: fix repeated words in comments - lart: remove driver - tplink: - Add TP-Link SafeLoader partitions table parser and bindings - Describe TP-Link SafeLoader parser - Describe TP-Link SafeLoader dynamic subpartitions - mtdoops: - Panic caused mtdoops to call mtdoops_erase function immediately - Add mtdoops_erase function and move mtdoops_inc_counter after it - Change printk() to counterpart pr_ functions MTD binding cleanup: - Fixed-partitions: Fix 'sercomm,scpart-id' schema - Standardize the style in the examples - Drop object types when referencing other files - Argue in favor of keeping additionalProperties set to true - NVMEM-cells: - Inherit from MTD partitions - Drop range property from example - Partitions: - Change qcom,smem-part partition type - Constrain the list of parsers - Physmap: Reuse the generic definitions - SPI-NOR: Drop common properties - Sunxi-nand: Add an example to validate the bindings - Onenand: Mention the expected node name - Ingenic: Mark partitions in the controller node as deprecated - NAND: - Standardize the child node name - Drop common properties already defined in generic files - nand-chip.yaml should reference mtd.yaml - Remove useless file about partitions - Clarify all partition subnodes SPI NOR core changes: - Add support for flash reset using the dt reset-gpios property. - Update hwcaps.mask to include 8D-8D-8D read and page program ops when xSPI profile 1.0 table is defined. - Bypass zero erase size in spi_nor_find_best_erase_type(). - Fix select_uniform_erase to skip 0 erase size - Add generic flash driver. If a flash is not found in the flash_info array, fall back to the generic flash driver which is described solely by the flash's SFDP tables. - Fix the number of bytes for the dummy cycles in spi_nor_spimem_check_readop(). - Introduce SPI_NOR_QUAD_PP flag, as PP_1_1_4 is not SFDP discoverable. SPI NOR manufacturer drivers changes: - Spansion: - use PARSE_SFDP for s28hs512t, - add support for s28hl512t, s28hl01gt, and s28hs01gt. - Gigadevice: Replace default_init() with post_bfpt() for gd25q256. - Micron - ST: Enable locking for mt25qu256a. - Winbond: Add support for W25Q512NW-IQ. - ISSI: Use PARSE_SFDP and SPI_NOR_QUAD_PP. Raw NAND core changes: - Drop obsolete dependencies on COMPILE_TEST - MAINTAINERS: rectify entry for MESON NAND controller bindings - Drop EXPORT_SYMBOL_GPL for nanddev_erase() Raw NAND driver changes: - marvell: Enable NFC/DEVBUS arbiter - gpmi: Use pm_runtime_resume_and_get instead of pm_runtime_get_sync - mpc5121: Replace NO_IRQ by 0 - lpc32xx_{slc,mlc}: - Switch to using pm_ptr() - Switch to using gpiod API - lpc32xx_mlc: Switch to using pm_ptr() - cadence: Support 64-bit slave dma interface - rockchip: Describe rk3128-nfc in the bindings - brcmnand: Update interrupts description in the bindings SPI-NAND driver changes: - winbond: - Add Winbond W25N02KV flash support - Fix flash identification" * tag 'mtd/for-6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux: (76 commits) mtd: rawnand: Drop obsolete dependencies on COMPILE_TEST mtd: maps: pxa2xx-flash: fix memory leak in probe mtd: core: Fix refcount error in del_mtd_device() mtd: spi-nor: add SFDP fixups for Quad Page Program mtd: spi-nor: issi: is25wp256: Init flash based on SFDP mtd: spi-nor: winbond: add support for W25Q512NW-IQ mtd: spi-nor: micron-st: Enable locking for mt25qu256a mtd: spi-nor: Fix the number of bytes for the dummy cycles mtd: spi-nor: gigadevice: gd25q256: replace gd25q256_default_init with gd25q256_post_bfpt mtd: spi-nor: Fix formatting in spi_nor_read_raw() kerneldoc comment mtd: spi-nor: sysfs: print JEDEC ID for generic flash driver mtd: spi-nor: add generic flash driver mtd: spi-nor: fix select_uniform_erase to skip 0 erase size mtd: spi-nor: move function declaration out of sfdp.h mtd: spi-nor: remember full JEDEC flash ID mtd: spi-nor: sysfs: hide manufacturer if it is not set mtd: spi-nor: hide jedec_id sysfs attribute if not present mtd: spi-nor: Check for zero erase size in spi_nor_find_best_erase_type() mtd: rawnand: marvell: Enable NFC/DEVBUS arbiter mtd: parsers: refer to ARCH_BCMBCA instead of ARCH_BCM4908 ...
This commit is contained in:
commit
1e4fa020d5
@ -5,6 +5,9 @@ Contact: linux-mtd@lists.infradead.org
|
||||
Description: (RO) The JEDEC ID of the SPI NOR flash as reported by the
|
||||
flash device.
|
||||
|
||||
The attribute is not present if the flash doesn't support
|
||||
the "Read JEDEC ID" command (9Fh). This is the case for
|
||||
non-JEDEC compliant flashes.
|
||||
|
||||
What: /sys/bus/spi/devices/.../spi-nor/manufacturer
|
||||
Date: April 2021
|
||||
@ -12,6 +15,9 @@ KernelVersion: 5.14
|
||||
Contact: linux-mtd@lists.infradead.org
|
||||
Description: (RO) Manufacturer of the SPI NOR flash.
|
||||
|
||||
The attribute is not present if the flash device isn't
|
||||
known to the kernel and is only probed by its SFDP
|
||||
tables.
|
||||
|
||||
What: /sys/bus/spi/devices/.../spi-nor/partname
|
||||
Date: April 2021
|
||||
|
@ -14,9 +14,6 @@ maintainers:
|
||||
- Maxime Ripard <mripard@kernel.org>
|
||||
|
||||
properties:
|
||||
"#address-cells": true
|
||||
"#size-cells": true
|
||||
|
||||
compatible:
|
||||
enum:
|
||||
- allwinner,sun4i-a10-nand
|
||||
@ -49,12 +46,8 @@ properties:
|
||||
dma-names:
|
||||
const: rxtx
|
||||
|
||||
pinctrl-names: true
|
||||
|
||||
patternProperties:
|
||||
"^pinctrl-[0-9]+$": true
|
||||
|
||||
"^nand@[a-f0-9]+$":
|
||||
"^nand@[a-f0-9]$":
|
||||
type: object
|
||||
properties:
|
||||
reg:
|
||||
@ -91,6 +84,29 @@ required:
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
additionalProperties: false
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/sun6i-rtc.h>
|
||||
#include <dt-bindings/clock/sun8i-a23-a33-ccu.h>
|
||||
#include <dt-bindings/reset/sun8i-a23-a33-ccu.h>
|
||||
|
||||
nand-controller@1c03000 {
|
||||
compatible = "allwinner,sun8i-a23-nand-controller";
|
||||
reg = <0x01c03000 0x1000>;
|
||||
interrupts = <GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&ccu CLK_BUS_NAND>, <&ccu CLK_NAND>;
|
||||
clock-names = "ahb", "mod";
|
||||
resets = <&ccu RST_BUS_NAND>;
|
||||
reset-names = "ahb";
|
||||
dmas = <&dma 5>;
|
||||
dma-names = "rxtx";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&nand_pins &nand_cs0_pin &nand_rb0_pin>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
|
||||
...
|
||||
|
@ -35,9 +35,6 @@ properties:
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
"#address-cells": true
|
||||
"#size-cells": true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
@ -45,7 +42,7 @@ required:
|
||||
- clock-names
|
||||
- interrupts
|
||||
|
||||
additionalProperties: true
|
||||
unevaluatedProperties: true
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
@ -34,20 +34,20 @@ unevaluatedProperties: false
|
||||
examples:
|
||||
- |
|
||||
smcc: memory-controller@e000e000 {
|
||||
compatible = "arm,pl353-smc-r2p1", "arm,primecell";
|
||||
reg = <0xe000e000 0x0001000>;
|
||||
clock-names = "memclk", "apb_pclk";
|
||||
clocks = <&clkc 11>, <&clkc 44>;
|
||||
ranges = <0x0 0x0 0xe1000000 0x1000000 /* Nand CS region */
|
||||
0x1 0x0 0xe2000000 0x2000000 /* SRAM/NOR CS0 region */
|
||||
0x2 0x0 0xe4000000 0x2000000>; /* SRAM/NOR CS1 region */
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
compatible = "arm,pl353-smc-r2p1", "arm,primecell";
|
||||
reg = <0xe000e000 0x0001000>;
|
||||
clock-names = "memclk", "apb_pclk";
|
||||
clocks = <&clkc 11>, <&clkc 44>;
|
||||
ranges = <0x0 0x0 0xe1000000 0x1000000 /* Nand CS region */
|
||||
0x1 0x0 0xe2000000 0x2000000 /* SRAM/NOR CS0 region */
|
||||
0x2 0x0 0xe4000000 0x2000000>; /* SRAM/NOR CS1 region */
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
|
||||
nfc0: nand-controller@0,0 {
|
||||
compatible = "arm,pl353-nand-r2p1";
|
||||
reg = <0 0 0x1000000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
nfc0: nand-controller@0,0 {
|
||||
compatible = "arm,pl353-nand-r2p1";
|
||||
reg = <0 0 0x1000000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
@ -45,10 +45,8 @@ Optional properties:
|
||||
- atmel,rb: an integer identifying the native Ready/Busy pin. Only meaningful
|
||||
on sama5 SoCs.
|
||||
|
||||
All generic properties described in
|
||||
Documentation/devicetree/bindings/mtd/{common,nand}.txt also apply to the NAND
|
||||
device node, and NAND partitions should be defined under the NAND node as
|
||||
described in Documentation/devicetree/bindings/mtd/partition.txt.
|
||||
All generic properties are described in the generic yaml files under
|
||||
Documentation/devicetree/bindings/mtd/.
|
||||
|
||||
* ECC engine (PMECC) bindings:
|
||||
|
||||
|
@ -86,15 +86,15 @@ properties:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: NAND CTLRDY interrupt
|
||||
- description: FLASH_DMA_DONE if flash DMA is available
|
||||
- description: FLASH_EDU_DONE if EDU is available
|
||||
- description: FLASH_DMA_DONE (if flash DMA is available) or FLASH_EDU_DONE (if EDU is available)
|
||||
|
||||
interrupt-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: nand_ctlrdy
|
||||
- const: flash_dma_done
|
||||
- const: flash_edu_done
|
||||
- enum:
|
||||
- flash_dma_done
|
||||
- flash_edu_done
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
@ -173,6 +173,13 @@ allOf:
|
||||
- const: nand
|
||||
- const: iproc-idm
|
||||
- const: iproc-ext
|
||||
- if:
|
||||
properties:
|
||||
interrupts:
|
||||
minItems: 2
|
||||
then:
|
||||
required:
|
||||
- interrupt-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
@ -184,51 +191,52 @@ required:
|
||||
examples:
|
||||
- |
|
||||
nand-controller@f0442800 {
|
||||
compatible = "brcm,brcmnand-v7.0", "brcm,brcmnand";
|
||||
reg = <0xf0442800 0x600>,
|
||||
<0xf0443000 0x100>;
|
||||
reg-names = "nand", "flash-dma";
|
||||
interrupt-parent = <&hif_intr2_intc>;
|
||||
interrupts = <24>, <4>;
|
||||
compatible = "brcm,brcmnand-v7.0", "brcm,brcmnand";
|
||||
reg = <0xf0442800 0x600>,
|
||||
<0xf0443000 0x100>;
|
||||
reg-names = "nand", "flash-dma";
|
||||
interrupt-parent = <&hif_intr2_intc>;
|
||||
interrupts = <24>, <4>;
|
||||
interrupt-names = "nand_ctlrdy", "flash_dma_done";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
nand@1 {
|
||||
compatible = "brcm,nandcs";
|
||||
reg = <1>; // Chip select 1
|
||||
nand-on-flash-bbt;
|
||||
nand-ecc-strength = <12>;
|
||||
nand-ecc-step-size = <512>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
nand@1 {
|
||||
compatible = "brcm,nandcs";
|
||||
reg = <1>; // Chip select 1
|
||||
nand-on-flash-bbt;
|
||||
nand-ecc-strength = <12>;
|
||||
nand-ecc-step-size = <512>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
};
|
||||
#size-cells = <1>;
|
||||
};
|
||||
};
|
||||
- |
|
||||
nand-controller@10000200 {
|
||||
compatible = "brcm,nand-bcm63168", "brcm,nand-bcm6368",
|
||||
"brcm,brcmnand-v4.0", "brcm,brcmnand";
|
||||
reg = <0x10000200 0x180>,
|
||||
<0x100000b0 0x10>,
|
||||
<0x10000600 0x200>;
|
||||
reg-names = "nand", "nand-int-base", "nand-cache";
|
||||
interrupt-parent = <&periph_intc>;
|
||||
interrupts = <50>;
|
||||
clocks = <&periph_clk 20>;
|
||||
clock-names = "nand";
|
||||
compatible = "brcm,nand-bcm63168", "brcm,nand-bcm6368",
|
||||
"brcm,brcmnand-v4.0", "brcm,brcmnand";
|
||||
reg = <0x10000200 0x180>,
|
||||
<0x100000b0 0x10>,
|
||||
<0x10000600 0x200>;
|
||||
reg-names = "nand", "nand-int-base", "nand-cache";
|
||||
interrupt-parent = <&periph_intc>;
|
||||
interrupts = <50>;
|
||||
clocks = <&periph_clk 20>;
|
||||
clock-names = "nand";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
nand@0 {
|
||||
compatible = "brcm,nandcs";
|
||||
reg = <0>;
|
||||
nand-on-flash-bbt;
|
||||
nand-ecc-strength = <1>;
|
||||
nand-ecc-step-size = <512>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
nand@0 {
|
||||
compatible = "brcm,nandcs";
|
||||
reg = <0>;
|
||||
nand-on-flash-bbt;
|
||||
nand-ecc-strength = <1>;
|
||||
nand-ecc-step-size = <512>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
};
|
||||
#size-cells = <1>;
|
||||
};
|
||||
};
|
||||
|
@ -145,6 +145,6 @@ examples:
|
||||
#size-cells = <0>;
|
||||
|
||||
nand@0 {
|
||||
reg = <0>;
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
@ -32,9 +32,9 @@ properties:
|
||||
|
||||
partitions:
|
||||
type: object
|
||||
deprecated: true
|
||||
description:
|
||||
Node containing description of fixed partitions.
|
||||
See Documentation/devicetree/bindings/mtd/partition.txt
|
||||
|
||||
patternProperties:
|
||||
"^nand@[a-f0-9]$":
|
||||
@ -58,78 +58,78 @@ examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/ingenic,jz4780-cgu.h>
|
||||
memory-controller@13410000 {
|
||||
compatible = "ingenic,jz4780-nemc";
|
||||
reg = <0x13410000 0x10000>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
ranges = <1 0 0x1b000000 0x1000000>,
|
||||
<2 0 0x1a000000 0x1000000>,
|
||||
<3 0 0x19000000 0x1000000>,
|
||||
<4 0 0x18000000 0x1000000>,
|
||||
<5 0 0x17000000 0x1000000>,
|
||||
<6 0 0x16000000 0x1000000>;
|
||||
compatible = "ingenic,jz4780-nemc";
|
||||
reg = <0x13410000 0x10000>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
ranges = <1 0 0x1b000000 0x1000000>,
|
||||
<2 0 0x1a000000 0x1000000>,
|
||||
<3 0 0x19000000 0x1000000>,
|
||||
<4 0 0x18000000 0x1000000>,
|
||||
<5 0 0x17000000 0x1000000>,
|
||||
<6 0 0x16000000 0x1000000>;
|
||||
|
||||
clocks = <&cgu JZ4780_CLK_NEMC>;
|
||||
clocks = <&cgu JZ4780_CLK_NEMC>;
|
||||
|
||||
nand-controller@1 {
|
||||
compatible = "ingenic,jz4780-nand";
|
||||
reg = <1 0 0x1000000>;
|
||||
nand-controller@1 {
|
||||
compatible = "ingenic,jz4780-nand";
|
||||
reg = <1 0 0x1000000>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ecc-engine = <&bch>;
|
||||
ecc-engine = <&bch>;
|
||||
|
||||
ingenic,nemc-tAS = <10>;
|
||||
ingenic,nemc-tAH = <5>;
|
||||
ingenic,nemc-tBP = <10>;
|
||||
ingenic,nemc-tAW = <15>;
|
||||
ingenic,nemc-tSTRV = <100>;
|
||||
ingenic,nemc-tAS = <10>;
|
||||
ingenic,nemc-tAH = <5>;
|
||||
ingenic,nemc-tBP = <10>;
|
||||
ingenic,nemc-tAW = <15>;
|
||||
ingenic,nemc-tSTRV = <100>;
|
||||
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pins_nemc>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pins_nemc>;
|
||||
|
||||
nand@1 {
|
||||
reg = <1>;
|
||||
nand@1 {
|
||||
reg = <1>;
|
||||
|
||||
nand-ecc-step-size = <1024>;
|
||||
nand-ecc-strength = <24>;
|
||||
nand-ecc-mode = "hw";
|
||||
nand-on-flash-bbt;
|
||||
nand-ecc-step-size = <1024>;
|
||||
nand-ecc-strength = <24>;
|
||||
nand-ecc-mode = "hw";
|
||||
nand-on-flash-bbt;
|
||||
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pins_nemc_cs1>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pins_nemc_cs1>;
|
||||
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
partition@0 {
|
||||
label = "u-boot-spl";
|
||||
reg = <0x0 0x0 0x0 0x800000>;
|
||||
partition@0 {
|
||||
label = "u-boot-spl";
|
||||
reg = <0x0 0x0 0x0 0x800000>;
|
||||
};
|
||||
|
||||
partition@800000 {
|
||||
label = "u-boot";
|
||||
reg = <0x0 0x800000 0x0 0x200000>;
|
||||
};
|
||||
|
||||
partition@a00000 {
|
||||
label = "u-boot-env";
|
||||
reg = <0x0 0xa00000 0x0 0x200000>;
|
||||
};
|
||||
|
||||
partition@c00000 {
|
||||
label = "boot";
|
||||
reg = <0x0 0xc00000 0x0 0x4000000>;
|
||||
};
|
||||
|
||||
partition@4c00000 {
|
||||
label = "system";
|
||||
reg = <0x0 0x4c00000 0x1 0xfb400000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
partition@800000 {
|
||||
label = "u-boot";
|
||||
reg = <0x0 0x800000 0x0 0x200000>;
|
||||
};
|
||||
|
||||
partition@a00000 {
|
||||
label = "u-boot-env";
|
||||
reg = <0x0 0xa00000 0x0 0x200000>;
|
||||
};
|
||||
|
||||
partition@c00000 {
|
||||
label = "boot";
|
||||
reg = <0x0 0xc00000 0x0 0x4000000>;
|
||||
};
|
||||
|
||||
partition@4c00000 {
|
||||
label = "system";
|
||||
reg = <0x0 0x4c00000 0x1 0xfb400000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -39,14 +39,8 @@ properties:
|
||||
- const: tx
|
||||
- const: rx
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
"^nand@[a-f0-9]+$":
|
||||
"^nand@[a-f0-9]$":
|
||||
type: object
|
||||
properties:
|
||||
reg:
|
||||
@ -67,33 +61,31 @@ required:
|
||||
- clocks
|
||||
- dmas
|
||||
- dma-names
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
|
||||
additionalProperties: false
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
nand-controller@e0f00000 {
|
||||
compatible = "intel,lgm-ebunand";
|
||||
reg = <0xe0f00000 0x100>,
|
||||
<0xe1000000 0x300>,
|
||||
<0xe1400000 0x8000>,
|
||||
<0xe1c00000 0x1000>,
|
||||
<0x17400000 0x4>,
|
||||
<0x17c00000 0x4>;
|
||||
reg-names = "ebunand", "hsnand", "nand_cs0", "nand_cs1",
|
||||
"addr_sel0", "addr_sel1";
|
||||
clocks = <&cgu0 125>;
|
||||
dmas = <&dma0 8>, <&dma0 9>;
|
||||
dma-names = "tx", "rx";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "intel,lgm-ebunand";
|
||||
reg = <0xe0f00000 0x100>,
|
||||
<0xe1000000 0x300>,
|
||||
<0xe1400000 0x8000>,
|
||||
<0xe1c00000 0x1000>,
|
||||
<0x17400000 0x4>,
|
||||
<0x17c00000 0x4>;
|
||||
reg-names = "ebunand", "hsnand", "nand_cs0", "nand_cs1",
|
||||
"addr_sel0", "addr_sel1";
|
||||
clocks = <&cgu0 125>;
|
||||
dmas = <&dma0 8>, <&dma0 9>;
|
||||
dma-names = "tx", "rx";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
nand@0 {
|
||||
reg = <0>;
|
||||
nand-ecc-mode = "hw";
|
||||
};
|
||||
nand@0 {
|
||||
reg = <0>;
|
||||
nand-ecc-mode = "hw";
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
@ -70,24 +70,17 @@ properties:
|
||||
be used on such systems, to denote the absence of a reliable reset
|
||||
mechanism.
|
||||
|
||||
partitions:
|
||||
type: object
|
||||
|
||||
'#address-cells': true
|
||||
'#size-cells': true
|
||||
|
||||
patternProperties:
|
||||
# Note: use 'partitions' node for new users
|
||||
'^partition@':
|
||||
type: object
|
||||
|
||||
"^otp(-[0-9]+)?$":
|
||||
type: object
|
||||
reset-gpios:
|
||||
description:
|
||||
A GPIO line connected to the RESET (active low) signal of the device.
|
||||
If "broken-flash-reset" is present then having this property does not
|
||||
make any difference.
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
@ -97,6 +90,7 @@ examples:
|
||||
reg = <0>;
|
||||
spi-max-frequency = <40000000>;
|
||||
m25p,fast-read;
|
||||
reset-gpios = <&gpio 12 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
@ -19,7 +19,7 @@ accuracy:)
|
||||
- nxp,wr_low: WR_LOW
|
||||
|
||||
Optional subnodes:
|
||||
- Partitions, see Documentation/devicetree/bindings/mtd/partition.txt
|
||||
- Partitions, see Documentation/devicetree/bindings/mtd/mtd.yaml
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -20,7 +20,7 @@ clock speed:)
|
||||
- nxp,rsetup: Read setup time (R_SETUP)
|
||||
|
||||
Optional subnodes:
|
||||
- Partitions, see Documentation/devicetree/bindings/mtd/partition.txt
|
||||
- Partitions, see Documentation/devicetree/bindings/mtd/mtd.yaml
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -34,13 +34,13 @@ unevaluatedProperties: false
|
||||
examples:
|
||||
- |
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
eeram@0 {
|
||||
compatible = "microchip,48l640";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <20000000>;
|
||||
};
|
||||
eeram@0 {
|
||||
compatible = "microchip,48l640";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <20000000>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
@ -13,6 +13,9 @@ description: |
|
||||
Flash chips (Memory Technology Devices) are often used for solid state
|
||||
file systems on embedded devices.
|
||||
|
||||
allOf:
|
||||
- $ref: "mtd.yaml#"
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
@ -121,10 +124,6 @@ properties:
|
||||
big-endian: true
|
||||
little-endian: true
|
||||
|
||||
patternProperties:
|
||||
'@[0-9a-f]+$':
|
||||
$ref: partitions/partition.yaml
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -12,7 +12,7 @@ maintainers:
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: "^flash(@.*)?$"
|
||||
pattern: "^(flash|.*sram)(@.*)?$"
|
||||
|
||||
label:
|
||||
description:
|
||||
@ -21,9 +21,28 @@ properties:
|
||||
based name) in order to ease flash device identification and/or
|
||||
describe what they are used for.
|
||||
|
||||
'#address-cells':
|
||||
deprecated: true
|
||||
|
||||
'#size-cells':
|
||||
deprecated: true
|
||||
|
||||
partitions:
|
||||
$ref: /schemas/mtd/partitions/partitions.yaml
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
patternProperties:
|
||||
"@[0-9a-f]+$":
|
||||
$ref: partitions/partition.yaml
|
||||
deprecated: true
|
||||
|
||||
"^partition@[0-9a-f]+":
|
||||
$ref: partitions/partition.yaml
|
||||
deprecated: true
|
||||
|
||||
"^otp(-[0-9]+)?$":
|
||||
type: object
|
||||
$ref: ../nvmem/nvmem.yaml#
|
||||
|
||||
description: |
|
||||
@ -40,6 +59,7 @@ patternProperties:
|
||||
required:
|
||||
- compatible
|
||||
|
||||
# This is a generic file other binding inherit from
|
||||
additionalProperties: true
|
||||
|
||||
examples:
|
||||
|
@ -131,7 +131,7 @@ Example:
|
||||
};
|
||||
|
||||
NAND chip optional subnodes:
|
||||
- Partitions, see Documentation/devicetree/bindings/mtd/partition.txt
|
||||
- Partitions, see Documentation/devicetree/bindings/mtd/mtd.yaml
|
||||
|
||||
Example:
|
||||
nand@0 {
|
||||
|
@ -9,6 +9,9 @@ title: NAND Chip and NAND Controller Generic Binding
|
||||
maintainers:
|
||||
- Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
|
||||
allOf:
|
||||
- $ref: "mtd.yaml#"
|
||||
|
||||
description: |
|
||||
This file covers the generic description of a NAND chip. It implies that the
|
||||
bus interface should not be taken into account: both raw NAND devices and
|
||||
@ -67,4 +70,5 @@ properties:
|
||||
required:
|
||||
- reg
|
||||
|
||||
# This file can be referenced by more specific devices (like spi-nands)
|
||||
additionalProperties: true
|
||||
|
@ -51,7 +51,6 @@ properties:
|
||||
|
||||
patternProperties:
|
||||
"^nand@[a-f0-9]$":
|
||||
type: object
|
||||
$ref: "nand-chip.yaml#"
|
||||
|
||||
properties:
|
||||
@ -130,6 +129,7 @@ required:
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
|
||||
# This is a generic file other binding inherit from and extend
|
||||
additionalProperties: true
|
||||
|
||||
examples:
|
||||
|
@ -1,33 +0,0 @@
|
||||
Flash partitions in device tree
|
||||
===============================
|
||||
|
||||
Flash devices can be partitioned into one or more functional ranges (e.g. "boot
|
||||
code", "nvram", "kernel").
|
||||
|
||||
Different devices may be partitioned in a different ways. Some may use a fixed
|
||||
flash layout set at production time. Some may use on-flash table that describes
|
||||
the geometry and naming/purpose of each functional region. It is also possible
|
||||
to see these methods mixed.
|
||||
|
||||
To assist system software in locating partitions, we allow describing which
|
||||
method is used for a given flash device. To describe the method there should be
|
||||
a subnode of the flash device that is named 'partitions'. It must have a
|
||||
'compatible' property, which is used to identify the method to use.
|
||||
|
||||
When a single partition is represented with a DT node (it depends on a used
|
||||
format) it may also be described using above rules ('compatible' and optionally
|
||||
some extra properties / subnodes). It allows describing more complex,
|
||||
hierarchical (multi-level) layouts and should be used if there is some
|
||||
significant relation between partitions or some partition internally uses
|
||||
another partitioning method.
|
||||
|
||||
Available bindings are listed in the "partitions" subdirectory.
|
||||
|
||||
|
||||
Deprecated: partitions defined in flash node
|
||||
============================================
|
||||
|
||||
For backwards compatibility partitions as direct subnodes of the flash device are
|
||||
supported. This use is discouraged.
|
||||
NOTE: also for backwards compatibility, direct subnodes that have a compatible
|
||||
string are not considered partitions, as they may be used for other bindings.
|
@ -9,6 +9,8 @@ title: ARM Firmware Suite (AFS) Partitions
|
||||
maintainers:
|
||||
- Linus Walleij <linus.walleij@linaro.org>
|
||||
|
||||
select: false
|
||||
|
||||
description: |
|
||||
The ARM Firmware Suite is a flash partitioning system found on the
|
||||
ARM reference designs: Integrator AP, Integrator CP, Versatile AB,
|
||||
|
@ -17,6 +17,8 @@ description: |
|
||||
maintainers:
|
||||
- Rafał Miłecki <rafal@milecki.pl>
|
||||
|
||||
select: false
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: brcm,bcm4908-partitions
|
||||
|
@ -35,6 +35,8 @@ description: |
|
||||
maintainers:
|
||||
- Rafał Miłecki <rafal@milecki.pl>
|
||||
|
||||
select: false
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: brcm,bcm947xx-cfe-partitions
|
||||
|
@ -31,24 +31,17 @@ properties:
|
||||
|
||||
patternProperties:
|
||||
"@[0-9a-f]+$":
|
||||
allOf:
|
||||
- $ref: "partition.yaml#"
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: sercomm,sc-partitions
|
||||
then:
|
||||
properties:
|
||||
sercomm,scpart-id:
|
||||
description: Partition id in Sercomm partition map. Mtd
|
||||
parser uses this id to find a record in the partition map
|
||||
containing offset and size of the current partition. The
|
||||
values from partition map overrides partition offset and
|
||||
size defined in reg property of the dts. Frequently these
|
||||
values are the same, but may differ if device has bad
|
||||
eraseblocks on a flash.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
$ref: partition.yaml#
|
||||
|
||||
properties:
|
||||
sercomm,scpart-id:
|
||||
description: Partition id in Sercomm partition map. Mtd parser
|
||||
uses this id to find a record in the partition map containing
|
||||
offset and size of the current partition. The values from
|
||||
partition map overrides partition offset and size defined in
|
||||
reg property of the dts. Frequently these values are the same,
|
||||
but may differ if device has bad eraseblocks on a flash.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
required:
|
||||
- "#address-cells"
|
||||
@ -84,6 +77,7 @@ examples:
|
||||
partition@0 {
|
||||
label = "filesystem";
|
||||
reg = <0x00000000 0x1 0x00000000>;
|
||||
linux,rootfs;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -18,6 +18,8 @@ description: |
|
||||
maintainers:
|
||||
- Rafał Miłecki <rafal@milecki.pl>
|
||||
|
||||
select: false
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: linksys,ns-partitions
|
||||
|
@ -17,6 +17,7 @@ maintainers:
|
||||
- Ansuel Smith <ansuelsmth@gmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/mtd/partitions/partition.yaml#
|
||||
- $ref: /schemas/nvmem/nvmem.yaml#
|
||||
|
||||
properties:
|
||||
@ -26,7 +27,7 @@ properties:
|
||||
required:
|
||||
- compatible
|
||||
|
||||
additionalProperties: true
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
@ -84,7 +85,6 @@ examples:
|
||||
compatible = "nvmem-cells";
|
||||
label = "calibration";
|
||||
reg = <0xf00000 0x100000>;
|
||||
ranges = <0 0xf00000 0x100000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
|
@ -52,6 +52,10 @@ properties:
|
||||
immune to paired-pages corruptions
|
||||
type: boolean
|
||||
|
||||
linux,rootfs:
|
||||
description: Marks partition that contains root filesystem to mount and boot
|
||||
user space from
|
||||
|
||||
if:
|
||||
not:
|
||||
required: [ reg ]
|
||||
@ -60,4 +64,5 @@ then:
|
||||
$nodename:
|
||||
pattern: '^partition-.*$'
|
||||
|
||||
# This is a generic file other binding inherit from and extend
|
||||
additionalProperties: true
|
||||
|
@ -0,0 +1,41 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mtd/partitions/partitions.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Partitions
|
||||
|
||||
description: |
|
||||
This binding is generic and describes the content of the partitions container
|
||||
node. All partition parsers must be referenced here.
|
||||
|
||||
maintainers:
|
||||
- Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
|
||||
oneOf:
|
||||
- $ref: arm,arm-firmware-suite.yaml
|
||||
- $ref: brcm,bcm4908-partitions.yaml
|
||||
- $ref: brcm,bcm947xx-cfe-partitions.yaml
|
||||
- $ref: fixed-partitions.yaml
|
||||
- $ref: linksys,ns-partitions.yaml
|
||||
- $ref: qcom,smem-part.yaml
|
||||
- $ref: redboot-fis.yaml
|
||||
|
||||
properties:
|
||||
compatible: true
|
||||
|
||||
'#address-cells':
|
||||
enum: [1, 2]
|
||||
|
||||
'#size-cells':
|
||||
enum: [1, 2]
|
||||
|
||||
patternProperties:
|
||||
"partition(-.+|@[0-9a-f]+)":
|
||||
$ref: partition.yaml
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
unevaluatedProperties: false
|
@ -15,13 +15,15 @@ description: |
|
||||
varies between partition table revisions. V3 supports maximum 16 partitions
|
||||
and V4 supports 48 partitions.
|
||||
|
||||
select: false
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,smem-part
|
||||
|
||||
patternProperties:
|
||||
"^partition-[0-9a-z]+$":
|
||||
$ref: partition.yaml#
|
||||
$ref: nvmem-cells.yaml
|
||||
|
||||
required:
|
||||
- compatible
|
||||
@ -39,22 +41,22 @@ examples:
|
||||
- |
|
||||
/* Example declaring dynamic partition */
|
||||
flash {
|
||||
partitions {
|
||||
compatible = "qcom,smem-part";
|
||||
partitions {
|
||||
compatible = "qcom,smem-part";
|
||||
|
||||
partition-art {
|
||||
compatible = "nvmem-cells";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
label = "0:art";
|
||||
partition-art {
|
||||
compatible = "nvmem-cells";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
label = "0:art";
|
||||
|
||||
macaddr_art_0: macaddr@0 {
|
||||
reg = <0x0 0x6>;
|
||||
};
|
||||
macaddr_art_0: macaddr@0 {
|
||||
reg = <0x0 0x6>;
|
||||
};
|
||||
|
||||
macaddr_art_6: macaddr@6 {
|
||||
reg = <0x6 0x6>;
|
||||
};
|
||||
macaddr_art_6: macaddr@6 {
|
||||
reg = <0x6 0x6>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -16,6 +16,8 @@ description: The FLASH Image System (FIS) directory is a flash description
|
||||
maintainers:
|
||||
- Linus Walleij <linus.walleij@linaro.org>
|
||||
|
||||
select: false
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: redboot-fis
|
||||
@ -26,6 +28,10 @@ properties:
|
||||
device. On a flash memory with 32KB eraseblocks, 0 means the first
|
||||
eraseblock at 0x00000000, 1 means the second eraseblock at 0x00008000 and so on.
|
||||
|
||||
'#address-cells': false
|
||||
|
||||
'#size-cells': false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- fis-index-block
|
||||
|
@ -0,0 +1,57 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mtd/partitions/tplink,safeloader-partitions.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: TP-Link SafeLoader partitions
|
||||
|
||||
description: |
|
||||
TP-Link home routers store various data on flash (e.g. bootloader,
|
||||
flash layout, firmware, product info, configuration, calibration
|
||||
data). That requires flash partitioning.
|
||||
|
||||
Flash space layout of TP-Link devices is stored on flash itself using
|
||||
a custom ASCII-based format. That format was first found in TP-Link
|
||||
devices with a custom SafeLoader bootloader. Later it was adapted to
|
||||
CFE and U-Boot bootloaders.
|
||||
|
||||
Partitions specified in partitions table cover whole flash space. Some
|
||||
contain static data that shouldn't get modified (device's MAC or WiFi
|
||||
calibration data). Others are semi-static (like kernel). Finally some
|
||||
partitions contain fully changeable content (like rootfs).
|
||||
|
||||
This binding describes partitioning method and defines offset of ASCII
|
||||
based partitions table. That offset is picked at manufacturing process
|
||||
and doesn't change.
|
||||
|
||||
maintainers:
|
||||
- Rafał Miłecki <rafal@milecki.pl>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: tplink,safeloader-partitions
|
||||
|
||||
partitions-table-offset:
|
||||
description: Flash offset of partitions table
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
patternProperties:
|
||||
"^partition-.*$":
|
||||
$ref: partition.yaml#
|
||||
|
||||
required:
|
||||
- partitions-table-offset
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
partitions {
|
||||
compatible = "tplink,safeloader-partitions";
|
||||
partitions-table-offset = <0x100000>;
|
||||
|
||||
partition-file-system {
|
||||
linux,rootfs;
|
||||
};
|
||||
};
|
@ -31,9 +31,6 @@ properties:
|
||||
- const: core
|
||||
- const: aon
|
||||
|
||||
"#address-cells": true
|
||||
"#size-cells": true
|
||||
|
||||
patternProperties:
|
||||
"^nand@[a-f0-9]$":
|
||||
type: object
|
||||
@ -139,85 +136,85 @@ examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/qcom,gcc-ipq806x.h>
|
||||
nand-controller@1ac00000 {
|
||||
compatible = "qcom,ipq806x-nand";
|
||||
reg = <0x1ac00000 0x800>;
|
||||
compatible = "qcom,ipq806x-nand";
|
||||
reg = <0x1ac00000 0x800>;
|
||||
|
||||
clocks = <&gcc EBI2_CLK>,
|
||||
<&gcc EBI2_AON_CLK>;
|
||||
clock-names = "core", "aon";
|
||||
clocks = <&gcc EBI2_CLK>,
|
||||
<&gcc EBI2_AON_CLK>;
|
||||
clock-names = "core", "aon";
|
||||
|
||||
dmas = <&adm_dma 3>;
|
||||
dma-names = "rxtx";
|
||||
qcom,cmd-crci = <15>;
|
||||
qcom,data-crci = <3>;
|
||||
dmas = <&adm_dma 3>;
|
||||
dma-names = "rxtx";
|
||||
qcom,cmd-crci = <15>;
|
||||
qcom,data-crci = <3>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
nand@0 {
|
||||
reg = <0>;
|
||||
nand@0 {
|
||||
reg = <0>;
|
||||
|
||||
nand-ecc-strength = <4>;
|
||||
nand-bus-width = <8>;
|
||||
nand-ecc-strength = <4>;
|
||||
nand-bus-width = <8>;
|
||||
|
||||
qcom,boot-partitions = <0x0 0x58a0000>;
|
||||
qcom,boot-partitions = <0x0 0x58a0000>;
|
||||
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
partition@0 {
|
||||
label = "boot-nand";
|
||||
reg = <0 0x58a0000>;
|
||||
};
|
||||
partition@0 {
|
||||
label = "boot-nand";
|
||||
reg = <0 0x58a0000>;
|
||||
};
|
||||
|
||||
partition@58a0000 {
|
||||
label = "fs-nand";
|
||||
reg = <0x58a0000 0x4000000>;
|
||||
};
|
||||
partition@58a0000 {
|
||||
label = "fs-nand";
|
||||
reg = <0x58a0000 0x4000000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
#include <dt-bindings/clock/qcom,gcc-ipq4019.h>
|
||||
nand-controller@79b0000 {
|
||||
compatible = "qcom,ipq4019-nand";
|
||||
reg = <0x79b0000 0x1000>;
|
||||
compatible = "qcom,ipq4019-nand";
|
||||
reg = <0x79b0000 0x1000>;
|
||||
|
||||
clocks = <&gcc GCC_QPIC_CLK>,
|
||||
<&gcc GCC_QPIC_AHB_CLK>;
|
||||
clock-names = "core", "aon";
|
||||
clocks = <&gcc GCC_QPIC_CLK>,
|
||||
<&gcc GCC_QPIC_AHB_CLK>;
|
||||
clock-names = "core", "aon";
|
||||
|
||||
dmas = <&qpicbam 0>,
|
||||
<&qpicbam 1>,
|
||||
<&qpicbam 2>;
|
||||
dma-names = "tx", "rx", "cmd";
|
||||
dmas = <&qpicbam 0>,
|
||||
<&qpicbam 1>,
|
||||
<&qpicbam 2>;
|
||||
dma-names = "tx", "rx", "cmd";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
nand@0 {
|
||||
reg = <0>;
|
||||
nand-ecc-strength = <4>;
|
||||
nand-bus-width = <8>;
|
||||
nand@0 {
|
||||
reg = <0>;
|
||||
nand-ecc-strength = <4>;
|
||||
nand-bus-width = <8>;
|
||||
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
partition@0 {
|
||||
label = "boot-nand";
|
||||
reg = <0 0x58a0000>;
|
||||
};
|
||||
partition@0 {
|
||||
label = "boot-nand";
|
||||
reg = <0 0x58a0000>;
|
||||
};
|
||||
|
||||
partition@58a0000 {
|
||||
label = "fs-nand";
|
||||
reg = <0x58a0000 0x4000000>;
|
||||
};
|
||||
partition@58a0000 {
|
||||
label = "fs-nand";
|
||||
reg = <0x58a0000 0x4000000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
@ -19,7 +19,9 @@ properties:
|
||||
- const: rockchip,rk2928-nfc
|
||||
- const: rockchip,rv1108-nfc
|
||||
- items:
|
||||
- const: rockchip,rk3036-nfc
|
||||
- enum:
|
||||
- rockchip,rk3036-nfc
|
||||
- rockchip,rk3128-nfc
|
||||
- const: rockchip,rk2928-nfc
|
||||
- items:
|
||||
- const: rockchip,rk3308-nfc
|
||||
|
@ -101,31 +101,32 @@ examples:
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/stm32mp1-clks.h>
|
||||
#include <dt-bindings/reset/stm32mp1-resets.h>
|
||||
nand-controller@58002000 {
|
||||
compatible = "st,stm32mp15-fmc2";
|
||||
reg = <0x58002000 0x1000>,
|
||||
<0x80000000 0x1000>,
|
||||
<0x88010000 0x1000>,
|
||||
<0x88020000 0x1000>,
|
||||
<0x81000000 0x1000>,
|
||||
<0x89010000 0x1000>,
|
||||
<0x89020000 0x1000>;
|
||||
interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
|
||||
dmas = <&mdma1 20 0x2 0x12000a02 0x0 0x0>,
|
||||
<&mdma1 20 0x2 0x12000a08 0x0 0x0>,
|
||||
<&mdma1 21 0x2 0x12000a0a 0x0 0x0>;
|
||||
dma-names = "tx", "rx", "ecc";
|
||||
clocks = <&rcc FMC_K>;
|
||||
resets = <&rcc FMC_R>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
nand@0 {
|
||||
reg = <0>;
|
||||
nand-on-flash-bbt;
|
||||
nand-controller@58002000 {
|
||||
compatible = "st,stm32mp15-fmc2";
|
||||
reg = <0x58002000 0x1000>,
|
||||
<0x80000000 0x1000>,
|
||||
<0x88010000 0x1000>,
|
||||
<0x88020000 0x1000>,
|
||||
<0x81000000 0x1000>,
|
||||
<0x89010000 0x1000>,
|
||||
<0x89020000 0x1000>;
|
||||
interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
|
||||
dmas = <&mdma1 20 0x2 0x12000a02 0x0 0x0>,
|
||||
<&mdma1 20 0x2 0x12000a08 0x0 0x0>,
|
||||
<&mdma1 21 0x2 0x12000a0a 0x0 0x0>;
|
||||
dma-names = "tx", "rx", "ecc";
|
||||
clocks = <&rcc FMC_K>;
|
||||
resets = <&rcc FMC_R>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
};
|
||||
#size-cells = <0>;
|
||||
|
||||
nand@0 {
|
||||
reg = <0>;
|
||||
nand-on-flash-bbt;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
@ -44,26 +44,26 @@ additionalProperties: false
|
||||
examples:
|
||||
- |
|
||||
bus {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
hbmc: memory-controller@47034000 {
|
||||
compatible = "ti,am654-hbmc";
|
||||
reg = <0x0 0x47034000 0x0 0x100>,
|
||||
<0x5 0x00000000 0x1 0x0000000>;
|
||||
ranges = <0x0 0x0 0x5 0x00000000 0x4000000>, /* CS0 - 64MB */
|
||||
<0x1 0x0 0x5 0x04000000 0x4000000>; /* CS1 - 64MB */
|
||||
clocks = <&k3_clks 102 0>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
power-domains = <&k3_pds 55>;
|
||||
mux-controls = <&hbmc_mux 0>;
|
||||
#size-cells = <2>;
|
||||
|
||||
flash@0,0 {
|
||||
compatible = "cypress,hyperflash", "cfi-flash";
|
||||
reg = <0x0 0x0 0x4000000>;
|
||||
#address-cells = <1>;
|
||||
hbmc: memory-controller@47034000 {
|
||||
compatible = "ti,am654-hbmc";
|
||||
reg = <0x0 0x47034000 0x0 0x100>,
|
||||
<0x5 0x00000000 0x1 0x0000000>;
|
||||
ranges = <0x0 0x0 0x5 0x00000000 0x4000000>, /* CS0 - 64MB */
|
||||
<0x1 0x0 0x5 0x04000000 0x4000000>; /* CS1 - 64MB */
|
||||
clocks = <&k3_clks 102 0>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
power-domains = <&k3_pds 55>;
|
||||
mux-controls = <&hbmc_mux 0>;
|
||||
|
||||
flash@0,0 {
|
||||
compatible = "cypress,hyperflash", "cfi-flash";
|
||||
reg = <0x0 0x0 0x4000000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -15,6 +15,9 @@ description:
|
||||
as child nodes of the GPMC controller.
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: "^onenand@[0-9],[0,9]$"
|
||||
|
||||
compatible:
|
||||
const: ti,omap2-onenand
|
||||
|
||||
|
@ -13464,7 +13464,7 @@ MESON NAND CONTROLLER DRIVER FOR AMLOGIC SOCS
|
||||
M: Liang Yang <liang.yang@amlogic.com>
|
||||
L: linux-mtd@lists.infradead.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/mtd/amlogic,meson-nand.txt
|
||||
F: Documentation/devicetree/bindings/mtd/amlogic,meson-nand.yaml
|
||||
F: drivers/mtd/nand/raw/meson_*
|
||||
|
||||
MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS
|
||||
|
@ -136,14 +136,6 @@ config MTD_PHRAM
|
||||
doesn't have access to, memory beyond the mem=xxx limit, nvram,
|
||||
memory on the video card, etc...
|
||||
|
||||
config MTD_LART
|
||||
tristate "28F160xx flash driver for LART"
|
||||
depends on SA1100_LART
|
||||
help
|
||||
This enables the flash driver for LART. Please note that you do
|
||||
not need any mapping/chip driver for LART. This one does it all
|
||||
for you, so go disable all of those if you enabled some of them (:
|
||||
|
||||
config MTD_MTDRAM
|
||||
tristate "Test driver using RAM"
|
||||
help
|
||||
|
@ -9,7 +9,6 @@ obj-$(CONFIG_MTD_PHRAM) += phram.o
|
||||
obj-$(CONFIG_MTD_PMC551) += pmc551.o
|
||||
obj-$(CONFIG_MTD_MS02NV) += ms02-nv.o
|
||||
obj-$(CONFIG_MTD_MTDRAM) += mtdram.o
|
||||
obj-$(CONFIG_MTD_LART) += lart.o
|
||||
obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
|
||||
obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
|
||||
obj-$(CONFIG_MTD_MCHP23K256) += mchp23k256.o
|
||||
|
@ -1,682 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
/*
|
||||
* MTD driver for the 28F160F3 Flash Memory (non-CFI) on LART.
|
||||
*
|
||||
* Author: Abraham vd Merwe <abraham@2d3d.co.za>
|
||||
*
|
||||
* Copyright (c) 2001, 2d3D, Inc.
|
||||
*
|
||||
* References:
|
||||
*
|
||||
* [1] 3 Volt Fast Boot Block Flash Memory" Intel Datasheet
|
||||
* - Order Number: 290644-005
|
||||
* - January 2000
|
||||
*
|
||||
* [2] MTD internal API documentation
|
||||
* - http://www.linux-mtd.infradead.org/
|
||||
*
|
||||
* Limitations:
|
||||
*
|
||||
* Even though this driver is written for 3 Volt Fast Boot
|
||||
* Block Flash Memory, it is rather specific to LART. With
|
||||
* Minor modifications, notably the without data/address line
|
||||
* mangling and different bus settings, etc. it should be
|
||||
* trivial to adapt to other platforms.
|
||||
*
|
||||
* If somebody would sponsor me a different board, I'll
|
||||
* adapt the driver (:
|
||||
*/
|
||||
|
||||
/* debugging */
|
||||
//#define LART_DEBUG
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#ifndef CONFIG_SA1100_LART
|
||||
#error This is for LART architecture only
|
||||
#endif
|
||||
|
||||
static char module_name[] = "lart";
|
||||
|
||||
/*
|
||||
* These values is specific to 28Fxxxx3 flash memory.
|
||||
* See section 2.3.1 in "3 Volt Fast Boot Block Flash Memory" Intel Datasheet
|
||||
*/
|
||||
#define FLASH_BLOCKSIZE_PARAM (4096 * BUSWIDTH)
|
||||
#define FLASH_NUMBLOCKS_16m_PARAM 8
|
||||
#define FLASH_NUMBLOCKS_8m_PARAM 8
|
||||
|
||||
/*
|
||||
* These values is specific to 28Fxxxx3 flash memory.
|
||||
* See section 2.3.2 in "3 Volt Fast Boot Block Flash Memory" Intel Datasheet
|
||||
*/
|
||||
#define FLASH_BLOCKSIZE_MAIN (32768 * BUSWIDTH)
|
||||
#define FLASH_NUMBLOCKS_16m_MAIN 31
|
||||
#define FLASH_NUMBLOCKS_8m_MAIN 15
|
||||
|
||||
/*
|
||||
* These values are specific to LART
|
||||
*/
|
||||
|
||||
/* general */
|
||||
#define BUSWIDTH 4 /* don't change this - a lot of the code _will_ break if you change this */
|
||||
#define FLASH_OFFSET 0xe8000000 /* see linux/arch/arm/mach-sa1100/lart.c */
|
||||
|
||||
/* blob */
|
||||
#define NUM_BLOB_BLOCKS FLASH_NUMBLOCKS_16m_PARAM
|
||||
#define PART_BLOB_START 0x00000000
|
||||
#define PART_BLOB_LEN (NUM_BLOB_BLOCKS * FLASH_BLOCKSIZE_PARAM)
|
||||
|
||||
/* kernel */
|
||||
#define NUM_KERNEL_BLOCKS 7
|
||||
#define PART_KERNEL_START (PART_BLOB_START + PART_BLOB_LEN)
|
||||
#define PART_KERNEL_LEN (NUM_KERNEL_BLOCKS * FLASH_BLOCKSIZE_MAIN)
|
||||
|
||||
/* initial ramdisk */
|
||||
#define NUM_INITRD_BLOCKS 24
|
||||
#define PART_INITRD_START (PART_KERNEL_START + PART_KERNEL_LEN)
|
||||
#define PART_INITRD_LEN (NUM_INITRD_BLOCKS * FLASH_BLOCKSIZE_MAIN)
|
||||
|
||||
/*
|
||||
* See section 4.0 in "3 Volt Fast Boot Block Flash Memory" Intel Datasheet
|
||||
*/
|
||||
#define READ_ARRAY 0x00FF00FF /* Read Array/Reset */
|
||||
#define READ_ID_CODES 0x00900090 /* Read Identifier Codes */
|
||||
#define ERASE_SETUP 0x00200020 /* Block Erase */
|
||||
#define ERASE_CONFIRM 0x00D000D0 /* Block Erase and Program Resume */
|
||||
#define PGM_SETUP 0x00400040 /* Program */
|
||||
#define STATUS_READ 0x00700070 /* Read Status Register */
|
||||
#define STATUS_CLEAR 0x00500050 /* Clear Status Register */
|
||||
#define STATUS_BUSY 0x00800080 /* Write State Machine Status (WSMS) */
|
||||
#define STATUS_ERASE_ERR 0x00200020 /* Erase Status (ES) */
|
||||
#define STATUS_PGM_ERR 0x00100010 /* Program Status (PS) */
|
||||
|
||||
/*
|
||||
* See section 4.2 in "3 Volt Fast Boot Block Flash Memory" Intel Datasheet
|
||||
*/
|
||||
#define FLASH_MANUFACTURER 0x00890089
|
||||
#define FLASH_DEVICE_8mbit_TOP 0x88f188f1
|
||||
#define FLASH_DEVICE_8mbit_BOTTOM 0x88f288f2
|
||||
#define FLASH_DEVICE_16mbit_TOP 0x88f388f3
|
||||
#define FLASH_DEVICE_16mbit_BOTTOM 0x88f488f4
|
||||
|
||||
/***************************************************************************************************/
|
||||
|
||||
/*
|
||||
* The data line mapping on LART is as follows:
|
||||
*
|
||||
* U2 CPU | U3 CPU
|
||||
* -------------------
|
||||
* 0 20 | 0 12
|
||||
* 1 22 | 1 14
|
||||
* 2 19 | 2 11
|
||||
* 3 17 | 3 9
|
||||
* 4 24 | 4 0
|
||||
* 5 26 | 5 2
|
||||
* 6 31 | 6 7
|
||||
* 7 29 | 7 5
|
||||
* 8 21 | 8 13
|
||||
* 9 23 | 9 15
|
||||
* 10 18 | 10 10
|
||||
* 11 16 | 11 8
|
||||
* 12 25 | 12 1
|
||||
* 13 27 | 13 3
|
||||
* 14 30 | 14 6
|
||||
* 15 28 | 15 4
|
||||
*/
|
||||
|
||||
/* Mangle data (x) */
|
||||
#define DATA_TO_FLASH(x) \
|
||||
( \
|
||||
(((x) & 0x08009000) >> 11) + \
|
||||
(((x) & 0x00002000) >> 10) + \
|
||||
(((x) & 0x04004000) >> 8) + \
|
||||
(((x) & 0x00000010) >> 4) + \
|
||||
(((x) & 0x91000820) >> 3) + \
|
||||
(((x) & 0x22080080) >> 2) + \
|
||||
((x) & 0x40000400) + \
|
||||
(((x) & 0x00040040) << 1) + \
|
||||
(((x) & 0x00110000) << 4) + \
|
||||
(((x) & 0x00220100) << 5) + \
|
||||
(((x) & 0x00800208) << 6) + \
|
||||
(((x) & 0x00400004) << 9) + \
|
||||
(((x) & 0x00000001) << 12) + \
|
||||
(((x) & 0x00000002) << 13) \
|
||||
)
|
||||
|
||||
/* Unmangle data (x) */
|
||||
#define FLASH_TO_DATA(x) \
|
||||
( \
|
||||
(((x) & 0x00010012) << 11) + \
|
||||
(((x) & 0x00000008) << 10) + \
|
||||
(((x) & 0x00040040) << 8) + \
|
||||
(((x) & 0x00000001) << 4) + \
|
||||
(((x) & 0x12200104) << 3) + \
|
||||
(((x) & 0x08820020) << 2) + \
|
||||
((x) & 0x40000400) + \
|
||||
(((x) & 0x00080080) >> 1) + \
|
||||
(((x) & 0x01100000) >> 4) + \
|
||||
(((x) & 0x04402000) >> 5) + \
|
||||
(((x) & 0x20008200) >> 6) + \
|
||||
(((x) & 0x80000800) >> 9) + \
|
||||
(((x) & 0x00001000) >> 12) + \
|
||||
(((x) & 0x00004000) >> 13) \
|
||||
)
|
||||
|
||||
/*
|
||||
* The address line mapping on LART is as follows:
|
||||
*
|
||||
* U3 CPU | U2 CPU
|
||||
* -------------------
|
||||
* 0 2 | 0 2
|
||||
* 1 3 | 1 3
|
||||
* 2 9 | 2 9
|
||||
* 3 13 | 3 8
|
||||
* 4 8 | 4 7
|
||||
* 5 12 | 5 6
|
||||
* 6 11 | 6 5
|
||||
* 7 10 | 7 4
|
||||
* 8 4 | 8 10
|
||||
* 9 5 | 9 11
|
||||
* 10 6 | 10 12
|
||||
* 11 7 | 11 13
|
||||
*
|
||||
* BOOT BLOCK BOUNDARY
|
||||
*
|
||||
* 12 15 | 12 15
|
||||
* 13 14 | 13 14
|
||||
* 14 16 | 14 16
|
||||
*
|
||||
* MAIN BLOCK BOUNDARY
|
||||
*
|
||||
* 15 17 | 15 18
|
||||
* 16 18 | 16 17
|
||||
* 17 20 | 17 20
|
||||
* 18 19 | 18 19
|
||||
* 19 21 | 19 21
|
||||
*
|
||||
* As we can see from above, the addresses aren't mangled across
|
||||
* block boundaries, so we don't need to worry about address
|
||||
* translations except for sending/reading commands during
|
||||
* initialization
|
||||
*/
|
||||
|
||||
/* Mangle address (x) on chip U2 */
|
||||
#define ADDR_TO_FLASH_U2(x) \
|
||||
( \
|
||||
(((x) & 0x00000f00) >> 4) + \
|
||||
(((x) & 0x00042000) << 1) + \
|
||||
(((x) & 0x0009c003) << 2) + \
|
||||
(((x) & 0x00021080) << 3) + \
|
||||
(((x) & 0x00000010) << 4) + \
|
||||
(((x) & 0x00000040) << 5) + \
|
||||
(((x) & 0x00000024) << 7) + \
|
||||
(((x) & 0x00000008) << 10) \
|
||||
)
|
||||
|
||||
/* Unmangle address (x) on chip U2 */
|
||||
#define FLASH_U2_TO_ADDR(x) \
|
||||
( \
|
||||
(((x) << 4) & 0x00000f00) + \
|
||||
(((x) >> 1) & 0x00042000) + \
|
||||
(((x) >> 2) & 0x0009c003) + \
|
||||
(((x) >> 3) & 0x00021080) + \
|
||||
(((x) >> 4) & 0x00000010) + \
|
||||
(((x) >> 5) & 0x00000040) + \
|
||||
(((x) >> 7) & 0x00000024) + \
|
||||
(((x) >> 10) & 0x00000008) \
|
||||
)
|
||||
|
||||
/* Mangle address (x) on chip U3 */
|
||||
#define ADDR_TO_FLASH_U3(x) \
|
||||
( \
|
||||
(((x) & 0x00000080) >> 3) + \
|
||||
(((x) & 0x00000040) >> 1) + \
|
||||
(((x) & 0x00052020) << 1) + \
|
||||
(((x) & 0x00084f03) << 2) + \
|
||||
(((x) & 0x00029010) << 3) + \
|
||||
(((x) & 0x00000008) << 5) + \
|
||||
(((x) & 0x00000004) << 7) \
|
||||
)
|
||||
|
||||
/* Unmangle address (x) on chip U3 */
|
||||
#define FLASH_U3_TO_ADDR(x) \
|
||||
( \
|
||||
(((x) << 3) & 0x00000080) + \
|
||||
(((x) << 1) & 0x00000040) + \
|
||||
(((x) >> 1) & 0x00052020) + \
|
||||
(((x) >> 2) & 0x00084f03) + \
|
||||
(((x) >> 3) & 0x00029010) + \
|
||||
(((x) >> 5) & 0x00000008) + \
|
||||
(((x) >> 7) & 0x00000004) \
|
||||
)
|
||||
|
||||
/***************************************************************************************************/
|
||||
|
||||
static __u8 read8 (__u32 offset)
|
||||
{
|
||||
volatile __u8 *data = (__u8 *) (FLASH_OFFSET + offset);
|
||||
#ifdef LART_DEBUG
|
||||
printk (KERN_DEBUG "%s(): 0x%.8x -> 0x%.2x\n", __func__, offset, *data);
|
||||
#endif
|
||||
return (*data);
|
||||
}
|
||||
|
||||
static __u32 read32 (__u32 offset)
|
||||
{
|
||||
volatile __u32 *data = (__u32 *) (FLASH_OFFSET + offset);
|
||||
#ifdef LART_DEBUG
|
||||
printk (KERN_DEBUG "%s(): 0x%.8x -> 0x%.8x\n", __func__, offset, *data);
|
||||
#endif
|
||||
return (*data);
|
||||
}
|
||||
|
||||
static void write32 (__u32 x,__u32 offset)
|
||||
{
|
||||
volatile __u32 *data = (__u32 *) (FLASH_OFFSET + offset);
|
||||
*data = x;
|
||||
#ifdef LART_DEBUG
|
||||
printk (KERN_DEBUG "%s(): 0x%.8x <- 0x%.8x\n", __func__, offset, *data);
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************************************/
|
||||
|
||||
/*
|
||||
* Probe for 16mbit flash memory on a LART board without doing
|
||||
* too much damage. Since we need to write 1 dword to memory,
|
||||
* we're f**cked if this happens to be DRAM since we can't
|
||||
* restore the memory (otherwise we might exit Read Array mode).
|
||||
*
|
||||
* Returns 1 if we found 16mbit flash memory on LART, 0 otherwise.
|
||||
*/
|
||||
static int flash_probe (void)
|
||||
{
|
||||
__u32 manufacturer,devtype;
|
||||
|
||||
/* setup "Read Identifier Codes" mode */
|
||||
write32 (DATA_TO_FLASH (READ_ID_CODES),0x00000000);
|
||||
|
||||
/* probe U2. U2/U3 returns the same data since the first 3
|
||||
* address lines is mangled in the same way */
|
||||
manufacturer = FLASH_TO_DATA (read32 (ADDR_TO_FLASH_U2 (0x00000000)));
|
||||
devtype = FLASH_TO_DATA (read32 (ADDR_TO_FLASH_U2 (0x00000001)));
|
||||
|
||||
/* put the flash back into command mode */
|
||||
write32 (DATA_TO_FLASH (READ_ARRAY),0x00000000);
|
||||
|
||||
return (manufacturer == FLASH_MANUFACTURER && (devtype == FLASH_DEVICE_16mbit_TOP || devtype == FLASH_DEVICE_16mbit_BOTTOM));
|
||||
}
|
||||
|
||||
/*
|
||||
* Erase one block of flash memory at offset ``offset'' which is any
|
||||
* address within the block which should be erased.
|
||||
*
|
||||
* Returns 1 if successful, 0 otherwise.
|
||||
*/
|
||||
static inline int erase_block (__u32 offset)
|
||||
{
|
||||
__u32 status;
|
||||
|
||||
#ifdef LART_DEBUG
|
||||
printk (KERN_DEBUG "%s(): 0x%.8x\n", __func__, offset);
|
||||
#endif
|
||||
|
||||
/* erase and confirm */
|
||||
write32 (DATA_TO_FLASH (ERASE_SETUP),offset);
|
||||
write32 (DATA_TO_FLASH (ERASE_CONFIRM),offset);
|
||||
|
||||
/* wait for block erase to finish */
|
||||
do
|
||||
{
|
||||
write32 (DATA_TO_FLASH (STATUS_READ),offset);
|
||||
status = FLASH_TO_DATA (read32 (offset));
|
||||
}
|
||||
while ((~status & STATUS_BUSY) != 0);
|
||||
|
||||
/* put the flash back into command mode */
|
||||
write32 (DATA_TO_FLASH (READ_ARRAY),offset);
|
||||
|
||||
/* was the erase successful? */
|
||||
if ((status & STATUS_ERASE_ERR))
|
||||
{
|
||||
printk (KERN_WARNING "%s: erase error at address 0x%.8x.\n",module_name,offset);
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
static int flash_erase (struct mtd_info *mtd,struct erase_info *instr)
|
||||
{
|
||||
__u32 addr,len;
|
||||
int i,first;
|
||||
|
||||
#ifdef LART_DEBUG
|
||||
printk (KERN_DEBUG "%s(addr = 0x%.8x, len = %d)\n", __func__, instr->addr, instr->len);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* check that both start and end of the requested erase are
|
||||
* aligned with the erasesize at the appropriate addresses.
|
||||
*
|
||||
* skip all erase regions which are ended before the start of
|
||||
* the requested erase. Actually, to save on the calculations,
|
||||
* we skip to the first erase region which starts after the
|
||||
* start of the requested erase, and then go back one.
|
||||
*/
|
||||
for (i = 0; i < mtd->numeraseregions && instr->addr >= mtd->eraseregions[i].offset; i++) ;
|
||||
i--;
|
||||
|
||||
/*
|
||||
* ok, now i is pointing at the erase region in which this
|
||||
* erase request starts. Check the start of the requested
|
||||
* erase range is aligned with the erase size which is in
|
||||
* effect here.
|
||||
*/
|
||||
if (i < 0 || (instr->addr & (mtd->eraseregions[i].erasesize - 1)))
|
||||
return -EINVAL;
|
||||
|
||||
/* Remember the erase region we start on */
|
||||
first = i;
|
||||
|
||||
/*
|
||||
* next, check that the end of the requested erase is aligned
|
||||
* with the erase region at that address.
|
||||
*
|
||||
* as before, drop back one to point at the region in which
|
||||
* the address actually falls
|
||||
*/
|
||||
for (; i < mtd->numeraseregions && instr->addr + instr->len >= mtd->eraseregions[i].offset; i++) ;
|
||||
i--;
|
||||
|
||||
/* is the end aligned on a block boundary? */
|
||||
if (i < 0 || ((instr->addr + instr->len) & (mtd->eraseregions[i].erasesize - 1)))
|
||||
return -EINVAL;
|
||||
|
||||
addr = instr->addr;
|
||||
len = instr->len;
|
||||
|
||||
i = first;
|
||||
|
||||
/* now erase those blocks */
|
||||
while (len)
|
||||
{
|
||||
if (!erase_block (addr))
|
||||
return (-EIO);
|
||||
|
||||
addr += mtd->eraseregions[i].erasesize;
|
||||
len -= mtd->eraseregions[i].erasesize;
|
||||
|
||||
if (addr == mtd->eraseregions[i].offset + (mtd->eraseregions[i].erasesize * mtd->eraseregions[i].numblocks)) i++;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int flash_read (struct mtd_info *mtd,loff_t from,size_t len,size_t *retlen,u_char *buf)
|
||||
{
|
||||
#ifdef LART_DEBUG
|
||||
printk (KERN_DEBUG "%s(from = 0x%.8x, len = %d)\n", __func__, (__u32)from, len);
|
||||
#endif
|
||||
|
||||
/* we always read len bytes */
|
||||
*retlen = len;
|
||||
|
||||
/* first, we read bytes until we reach a dword boundary */
|
||||
if (from & (BUSWIDTH - 1))
|
||||
{
|
||||
int gap = BUSWIDTH - (from & (BUSWIDTH - 1));
|
||||
|
||||
while (len && gap--) {
|
||||
*buf++ = read8 (from++);
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
/* now we read dwords until we reach a non-dword boundary */
|
||||
while (len >= BUSWIDTH)
|
||||
{
|
||||
*((__u32 *) buf) = read32 (from);
|
||||
|
||||
buf += BUSWIDTH;
|
||||
from += BUSWIDTH;
|
||||
len -= BUSWIDTH;
|
||||
}
|
||||
|
||||
/* top up the last unaligned bytes */
|
||||
if (len & (BUSWIDTH - 1))
|
||||
while (len--) *buf++ = read8 (from++);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write one dword ``x'' to flash memory at offset ``offset''. ``offset''
|
||||
* must be 32 bits, i.e. it must be on a dword boundary.
|
||||
*
|
||||
* Returns 1 if successful, 0 otherwise.
|
||||
*/
|
||||
static inline int write_dword (__u32 offset,__u32 x)
|
||||
{
|
||||
__u32 status;
|
||||
|
||||
#ifdef LART_DEBUG
|
||||
printk (KERN_DEBUG "%s(): 0x%.8x <- 0x%.8x\n", __func__, offset, x);
|
||||
#endif
|
||||
|
||||
/* setup writing */
|
||||
write32 (DATA_TO_FLASH (PGM_SETUP),offset);
|
||||
|
||||
/* write the data */
|
||||
write32 (x,offset);
|
||||
|
||||
/* wait for the write to finish */
|
||||
do
|
||||
{
|
||||
write32 (DATA_TO_FLASH (STATUS_READ),offset);
|
||||
status = FLASH_TO_DATA (read32 (offset));
|
||||
}
|
||||
while ((~status & STATUS_BUSY) != 0);
|
||||
|
||||
/* put the flash back into command mode */
|
||||
write32 (DATA_TO_FLASH (READ_ARRAY),offset);
|
||||
|
||||
/* was the write successful? */
|
||||
if ((status & STATUS_PGM_ERR) || read32 (offset) != x)
|
||||
{
|
||||
printk (KERN_WARNING "%s: write error at address 0x%.8x.\n",module_name,offset);
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
static int flash_write (struct mtd_info *mtd,loff_t to,size_t len,size_t *retlen,const u_char *buf)
|
||||
{
|
||||
__u8 tmp[4];
|
||||
int i,n;
|
||||
|
||||
#ifdef LART_DEBUG
|
||||
printk (KERN_DEBUG "%s(to = 0x%.8x, len = %d)\n", __func__, (__u32)to, len);
|
||||
#endif
|
||||
|
||||
/* sanity checks */
|
||||
if (!len) return (0);
|
||||
|
||||
/* first, we write a 0xFF.... padded byte until we reach a dword boundary */
|
||||
if (to & (BUSWIDTH - 1))
|
||||
{
|
||||
__u32 aligned = to & ~(BUSWIDTH - 1);
|
||||
int gap = to - aligned;
|
||||
|
||||
i = n = 0;
|
||||
|
||||
while (gap--) tmp[i++] = 0xFF;
|
||||
while (len && i < BUSWIDTH) {
|
||||
tmp[i++] = buf[n++];
|
||||
len--;
|
||||
}
|
||||
while (i < BUSWIDTH) tmp[i++] = 0xFF;
|
||||
|
||||
if (!write_dword (aligned,*((__u32 *) tmp))) return (-EIO);
|
||||
|
||||
to += n;
|
||||
buf += n;
|
||||
*retlen += n;
|
||||
}
|
||||
|
||||
/* now we write dwords until we reach a non-dword boundary */
|
||||
while (len >= BUSWIDTH)
|
||||
{
|
||||
if (!write_dword (to,*((__u32 *) buf))) return (-EIO);
|
||||
|
||||
to += BUSWIDTH;
|
||||
buf += BUSWIDTH;
|
||||
*retlen += BUSWIDTH;
|
||||
len -= BUSWIDTH;
|
||||
}
|
||||
|
||||
/* top up the last unaligned bytes, padded with 0xFF.... */
|
||||
if (len & (BUSWIDTH - 1))
|
||||
{
|
||||
i = n = 0;
|
||||
|
||||
while (len--) tmp[i++] = buf[n++];
|
||||
while (i < BUSWIDTH) tmp[i++] = 0xFF;
|
||||
|
||||
if (!write_dword (to,*((__u32 *) tmp))) return (-EIO);
|
||||
|
||||
*retlen += n;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/***************************************************************************************************/
|
||||
|
||||
static struct mtd_info mtd;
|
||||
|
||||
static struct mtd_erase_region_info erase_regions[] = {
|
||||
/* parameter blocks */
|
||||
{
|
||||
.offset = 0x00000000,
|
||||
.erasesize = FLASH_BLOCKSIZE_PARAM,
|
||||
.numblocks = FLASH_NUMBLOCKS_16m_PARAM,
|
||||
},
|
||||
/* main blocks */
|
||||
{
|
||||
.offset = FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM,
|
||||
.erasesize = FLASH_BLOCKSIZE_MAIN,
|
||||
.numblocks = FLASH_NUMBLOCKS_16m_MAIN,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct mtd_partition lart_partitions[] = {
|
||||
/* blob */
|
||||
{
|
||||
.name = "blob",
|
||||
.offset = PART_BLOB_START,
|
||||
.size = PART_BLOB_LEN,
|
||||
},
|
||||
/* kernel */
|
||||
{
|
||||
.name = "kernel",
|
||||
.offset = PART_KERNEL_START, /* MTDPART_OFS_APPEND */
|
||||
.size = PART_KERNEL_LEN,
|
||||
},
|
||||
/* initial ramdisk / file system */
|
||||
{
|
||||
.name = "file system",
|
||||
.offset = PART_INITRD_START, /* MTDPART_OFS_APPEND */
|
||||
.size = PART_INITRD_LEN, /* MTDPART_SIZ_FULL */
|
||||
}
|
||||
};
|
||||
#define NUM_PARTITIONS ARRAY_SIZE(lart_partitions)
|
||||
|
||||
static int __init lart_flash_init (void)
|
||||
{
|
||||
int result;
|
||||
memset (&mtd,0,sizeof (mtd));
|
||||
printk ("MTD driver for LART. Written by Abraham vd Merwe <abraham@2d3d.co.za>\n");
|
||||
printk ("%s: Probing for 28F160x3 flash on LART...\n",module_name);
|
||||
if (!flash_probe ())
|
||||
{
|
||||
printk (KERN_WARNING "%s: Found no LART compatible flash device\n",module_name);
|
||||
return (-ENXIO);
|
||||
}
|
||||
printk ("%s: This looks like a LART board to me.\n",module_name);
|
||||
mtd.name = module_name;
|
||||
mtd.type = MTD_NORFLASH;
|
||||
mtd.writesize = 1;
|
||||
mtd.writebufsize = 4;
|
||||
mtd.flags = MTD_CAP_NORFLASH;
|
||||
mtd.size = FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM + FLASH_BLOCKSIZE_MAIN * FLASH_NUMBLOCKS_16m_MAIN;
|
||||
mtd.erasesize = FLASH_BLOCKSIZE_MAIN;
|
||||
mtd.numeraseregions = ARRAY_SIZE(erase_regions);
|
||||
mtd.eraseregions = erase_regions;
|
||||
mtd._erase = flash_erase;
|
||||
mtd._read = flash_read;
|
||||
mtd._write = flash_write;
|
||||
mtd.owner = THIS_MODULE;
|
||||
|
||||
#ifdef LART_DEBUG
|
||||
printk (KERN_DEBUG
|
||||
"mtd.name = %s\n"
|
||||
"mtd.size = 0x%.8x (%uM)\n"
|
||||
"mtd.erasesize = 0x%.8x (%uK)\n"
|
||||
"mtd.numeraseregions = %d\n",
|
||||
mtd.name,
|
||||
mtd.size,mtd.size / (1024*1024),
|
||||
mtd.erasesize,mtd.erasesize / 1024,
|
||||
mtd.numeraseregions);
|
||||
|
||||
if (mtd.numeraseregions)
|
||||
for (result = 0; result < mtd.numeraseregions; result++)
|
||||
printk (KERN_DEBUG
|
||||
"\n\n"
|
||||
"mtd.eraseregions[%d].offset = 0x%.8x\n"
|
||||
"mtd.eraseregions[%d].erasesize = 0x%.8x (%uK)\n"
|
||||
"mtd.eraseregions[%d].numblocks = %d\n",
|
||||
result,mtd.eraseregions[result].offset,
|
||||
result,mtd.eraseregions[result].erasesize,mtd.eraseregions[result].erasesize / 1024,
|
||||
result,mtd.eraseregions[result].numblocks);
|
||||
|
||||
printk ("\npartitions = %d\n", ARRAY_SIZE(lart_partitions));
|
||||
|
||||
for (result = 0; result < ARRAY_SIZE(lart_partitions); result++)
|
||||
printk (KERN_DEBUG
|
||||
"\n\n"
|
||||
"lart_partitions[%d].name = %s\n"
|
||||
"lart_partitions[%d].offset = 0x%.8x\n"
|
||||
"lart_partitions[%d].size = 0x%.8x (%uK)\n",
|
||||
result,lart_partitions[result].name,
|
||||
result,lart_partitions[result].offset,
|
||||
result,lart_partitions[result].size,lart_partitions[result].size / 1024);
|
||||
#endif
|
||||
|
||||
result = mtd_device_register(&mtd, lart_partitions,
|
||||
ARRAY_SIZE(lart_partitions));
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
static void __exit lart_flash_exit (void)
|
||||
{
|
||||
mtd_device_unregister(&mtd);
|
||||
}
|
||||
|
||||
module_init (lart_flash_init);
|
||||
module_exit (lart_flash_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Abraham vd Merwe <abraham@2d3d.co.za>");
|
||||
MODULE_DESCRIPTION("MTD driver for Intel 28F160F3 on LART board");
|
@ -356,7 +356,7 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned
|
||||
* Newest unit in chain now contains data from _all_ older units.
|
||||
* So go through and erase each unit in chain, oldest first. (This
|
||||
* is important, by doing oldest first if we crash/reboot then it
|
||||
* it is relatively simple to clean up the mess).
|
||||
* is relatively simple to clean up the mess).
|
||||
*/
|
||||
pr_debug("INFTL: want to erase virtual chain %d\n", thisVUC);
|
||||
|
||||
|
@ -433,6 +433,8 @@ static int lpddr2_nvm_probe(struct platform_device *pdev)
|
||||
|
||||
/* lpddr2_nvm address range */
|
||||
add_range = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!add_range)
|
||||
return -ENODEV;
|
||||
|
||||
/* Populate map_info data structure */
|
||||
*map = (struct map_info) {
|
||||
|
@ -64,6 +64,7 @@ static int pxa2xx_flash_probe(struct platform_device *pdev)
|
||||
if (!info->map.virt) {
|
||||
printk(KERN_WARNING "Failed to ioremap %s\n",
|
||||
info->map.name);
|
||||
kfree(info);
|
||||
return -ENOMEM;
|
||||
}
|
||||
info->map.cached = ioremap_cache(info->map.phys, info->map.size);
|
||||
@ -85,6 +86,7 @@ static int pxa2xx_flash_probe(struct platform_device *pdev)
|
||||
iounmap((void *)info->map.virt);
|
||||
if (info->map.cached)
|
||||
iounmap(info->map.cached);
|
||||
kfree(info);
|
||||
return -EIO;
|
||||
}
|
||||
info->mtd->dev.parent = &pdev->dev;
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <linux/leds.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/nvmem-provider.h>
|
||||
#include <linux/root_dev.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
@ -551,22 +552,22 @@ static void mtd_check_of_node(struct mtd_info *mtd)
|
||||
struct device_node *partitions, *parent_dn, *mtd_dn = NULL;
|
||||
const char *pname, *prefix = "partition-";
|
||||
int plen, mtd_name_len, offset, prefix_len;
|
||||
struct mtd_info *parent;
|
||||
bool found = false;
|
||||
|
||||
/* Check if MTD already has a device node */
|
||||
if (dev_of_node(&mtd->dev))
|
||||
if (mtd_get_of_node(mtd))
|
||||
return;
|
||||
|
||||
/* Check if a partitions node exist */
|
||||
if (!mtd_is_partition(mtd))
|
||||
return;
|
||||
parent = mtd->parent;
|
||||
parent_dn = of_node_get(dev_of_node(&parent->dev));
|
||||
|
||||
parent_dn = of_node_get(mtd_get_of_node(mtd->parent));
|
||||
if (!parent_dn)
|
||||
return;
|
||||
|
||||
partitions = of_get_child_by_name(parent_dn, "partitions");
|
||||
if (mtd_is_partition(mtd->parent))
|
||||
partitions = of_node_get(parent_dn);
|
||||
else
|
||||
partitions = of_get_child_by_name(parent_dn, "partitions");
|
||||
if (!partitions)
|
||||
goto exit_parent;
|
||||
|
||||
@ -575,34 +576,26 @@ static void mtd_check_of_node(struct mtd_info *mtd)
|
||||
|
||||
/* Search if a partition is defined with the same name */
|
||||
for_each_child_of_node(partitions, mtd_dn) {
|
||||
offset = 0;
|
||||
|
||||
/* Skip partition with no/wrong prefix */
|
||||
if (!of_node_name_prefix(mtd_dn, "partition-"))
|
||||
if (!of_node_name_prefix(mtd_dn, prefix))
|
||||
continue;
|
||||
|
||||
/* Label have priority. Check that first */
|
||||
if (of_property_read_string(mtd_dn, "label", &pname)) {
|
||||
of_property_read_string(mtd_dn, "name", &pname);
|
||||
if (!of_property_read_string(mtd_dn, "label", &pname)) {
|
||||
offset = 0;
|
||||
} else {
|
||||
pname = mtd_dn->name;
|
||||
offset = prefix_len;
|
||||
}
|
||||
|
||||
plen = strlen(pname) - offset;
|
||||
if (plen == mtd_name_len &&
|
||||
!strncmp(mtd->name, pname + offset, plen)) {
|
||||
found = true;
|
||||
mtd_set_of_node(mtd, mtd_dn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
goto exit_partitions;
|
||||
|
||||
/* Set of_node only for nvmem */
|
||||
if (of_device_is_compatible(mtd_dn, "nvmem-cells"))
|
||||
mtd_set_of_node(mtd, mtd_dn);
|
||||
|
||||
exit_partitions:
|
||||
of_node_put(partitions);
|
||||
exit_parent:
|
||||
of_node_put(parent_dn);
|
||||
@ -723,8 +716,10 @@ int add_mtd_device(struct mtd_info *mtd)
|
||||
mtd_check_of_node(mtd);
|
||||
of_node_get(mtd_get_of_node(mtd));
|
||||
error = device_register(&mtd->dev);
|
||||
if (error)
|
||||
if (error) {
|
||||
put_device(&mtd->dev);
|
||||
goto fail_added;
|
||||
}
|
||||
|
||||
/* Add the nvmem provider */
|
||||
error = mtd_nvmem_add(mtd);
|
||||
@ -743,6 +738,17 @@ int add_mtd_device(struct mtd_info *mtd)
|
||||
not->add(mtd);
|
||||
|
||||
mutex_unlock(&mtd_table_mutex);
|
||||
|
||||
if (of_find_property(mtd_get_of_node(mtd), "linux,rootfs", NULL)) {
|
||||
if (IS_BUILTIN(CONFIG_MTD)) {
|
||||
pr_info("mtd: setting mtd%d (%s) as root device\n", mtd->index, mtd->name);
|
||||
ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, mtd->index);
|
||||
} else {
|
||||
pr_warn("mtd: can't set mtd%d (%s) as root device - mtd must be builtin\n",
|
||||
mtd->index, mtd->name);
|
||||
}
|
||||
}
|
||||
|
||||
/* We _know_ we aren't being removed, because
|
||||
our caller is still holding us here. So none
|
||||
of this try_ nonsense, and no bitching about it
|
||||
@ -774,6 +780,7 @@ int del_mtd_device(struct mtd_info *mtd)
|
||||
{
|
||||
int ret;
|
||||
struct mtd_notifier *not;
|
||||
struct device_node *mtd_of_node;
|
||||
|
||||
mutex_lock(&mtd_table_mutex);
|
||||
|
||||
@ -792,6 +799,7 @@ int del_mtd_device(struct mtd_info *mtd)
|
||||
mtd->index, mtd->name, mtd->usecount);
|
||||
ret = -EBUSY;
|
||||
} else {
|
||||
mtd_of_node = mtd_get_of_node(mtd);
|
||||
debugfs_remove_recursive(mtd->dbg.dfs_dir);
|
||||
|
||||
/* Try to remove the NVMEM provider */
|
||||
@ -803,7 +811,7 @@ int del_mtd_device(struct mtd_info *mtd)
|
||||
memset(&mtd->dev, 0, sizeof(mtd->dev));
|
||||
|
||||
idr_remove(&mtd_idr, mtd->index);
|
||||
of_node_put(mtd_get_of_node(mtd));
|
||||
of_node_put(mtd_of_node);
|
||||
|
||||
module_put(THIS_MODULE);
|
||||
ret = 0;
|
||||
@ -2483,6 +2491,7 @@ static int __init init_mtd(void)
|
||||
out_procfs:
|
||||
if (proc_mtd)
|
||||
remove_proc_entry("mtd", NULL);
|
||||
bdi_unregister(mtd_bdi);
|
||||
bdi_put(mtd_bdi);
|
||||
err_bdi:
|
||||
class_unregister(&mtd_class);
|
||||
|
@ -7,6 +7,8 @@
|
||||
* Author: Richard Purdie <rpurdie@openedhand.com>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/console.h>
|
||||
@ -93,9 +95,9 @@ static int mtdoops_erase_block(struct mtdoops_context *cxt, int offset)
|
||||
|
||||
ret = mtd_erase(mtd, &erase);
|
||||
if (ret) {
|
||||
printk(KERN_WARNING "mtdoops: erase of region [0x%llx, 0x%llx] on \"%s\" failed\n",
|
||||
(unsigned long long)erase.addr,
|
||||
(unsigned long long)erase.len, mtddev);
|
||||
pr_warn("erase of region [0x%llx, 0x%llx] on \"%s\" failed\n",
|
||||
(unsigned long long)erase.addr,
|
||||
(unsigned long long)erase.len, mtddev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -106,29 +108,8 @@ static int mtdoops_erase_block(struct mtdoops_context *cxt, int offset)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mtdoops_inc_counter(struct mtdoops_context *cxt)
|
||||
static void mtdoops_erase(struct mtdoops_context *cxt)
|
||||
{
|
||||
cxt->nextpage++;
|
||||
if (cxt->nextpage >= cxt->oops_pages)
|
||||
cxt->nextpage = 0;
|
||||
cxt->nextcount++;
|
||||
if (cxt->nextcount == 0xffffffff)
|
||||
cxt->nextcount = 0;
|
||||
|
||||
if (page_is_used(cxt, cxt->nextpage)) {
|
||||
schedule_work(&cxt->work_erase);
|
||||
return;
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "mtdoops: ready %d, %d (no erase)\n",
|
||||
cxt->nextpage, cxt->nextcount);
|
||||
}
|
||||
|
||||
/* Scheduled work - when we can't proceed without erasing a block */
|
||||
static void mtdoops_workfunc_erase(struct work_struct *work)
|
||||
{
|
||||
struct mtdoops_context *cxt =
|
||||
container_of(work, struct mtdoops_context, work_erase);
|
||||
struct mtd_info *mtd = cxt->mtd;
|
||||
int i = 0, j, ret, mod;
|
||||
|
||||
@ -145,20 +126,20 @@ static void mtdoops_workfunc_erase(struct work_struct *work)
|
||||
|
||||
while ((ret = mtd_block_isbad(mtd, cxt->nextpage * record_size)) > 0) {
|
||||
badblock:
|
||||
printk(KERN_WARNING "mtdoops: bad block at %08lx\n",
|
||||
cxt->nextpage * record_size);
|
||||
pr_warn("bad block at %08lx\n",
|
||||
cxt->nextpage * record_size);
|
||||
i++;
|
||||
cxt->nextpage = cxt->nextpage + (mtd->erasesize / record_size);
|
||||
if (cxt->nextpage >= cxt->oops_pages)
|
||||
cxt->nextpage = 0;
|
||||
if (i == cxt->oops_pages / (mtd->erasesize / record_size)) {
|
||||
printk(KERN_ERR "mtdoops: all blocks bad!\n");
|
||||
pr_err("all blocks bad!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "mtdoops: mtd_block_isbad failed, aborting\n");
|
||||
pr_err("mtd_block_isbad failed, aborting\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -166,21 +147,55 @@ badblock:
|
||||
ret = mtdoops_erase_block(cxt, cxt->nextpage * record_size);
|
||||
|
||||
if (ret >= 0) {
|
||||
printk(KERN_DEBUG "mtdoops: ready %d, %d\n",
|
||||
cxt->nextpage, cxt->nextcount);
|
||||
pr_debug("ready %d, %d\n",
|
||||
cxt->nextpage, cxt->nextcount);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ret == -EIO) {
|
||||
ret = mtd_block_markbad(mtd, cxt->nextpage * record_size);
|
||||
if (ret < 0 && ret != -EOPNOTSUPP) {
|
||||
printk(KERN_ERR "mtdoops: block_markbad failed, aborting\n");
|
||||
pr_err("block_markbad failed, aborting\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
goto badblock;
|
||||
}
|
||||
|
||||
/* Scheduled work - when we can't proceed without erasing a block */
|
||||
static void mtdoops_workfunc_erase(struct work_struct *work)
|
||||
{
|
||||
struct mtdoops_context *cxt =
|
||||
container_of(work, struct mtdoops_context, work_erase);
|
||||
mtdoops_erase(cxt);
|
||||
}
|
||||
|
||||
static void mtdoops_inc_counter(struct mtdoops_context *cxt, int panic)
|
||||
{
|
||||
cxt->nextpage++;
|
||||
if (cxt->nextpage >= cxt->oops_pages)
|
||||
cxt->nextpage = 0;
|
||||
cxt->nextcount++;
|
||||
if (cxt->nextcount == 0xffffffff)
|
||||
cxt->nextcount = 0;
|
||||
|
||||
if (page_is_used(cxt, cxt->nextpage)) {
|
||||
pr_debug("not ready %d, %d (erase %s)\n",
|
||||
cxt->nextpage, cxt->nextcount,
|
||||
panic ? "immediately" : "scheduled");
|
||||
if (panic) {
|
||||
/* In case of panic, erase immediately */
|
||||
mtdoops_erase(cxt);
|
||||
} else {
|
||||
/* Otherwise, schedule work to erase it "nicely" */
|
||||
schedule_work(&cxt->work_erase);
|
||||
}
|
||||
} else {
|
||||
pr_debug("ready %d, %d (no erase)\n",
|
||||
cxt->nextpage, cxt->nextcount);
|
||||
}
|
||||
}
|
||||
|
||||
static void mtdoops_write(struct mtdoops_context *cxt, int panic)
|
||||
{
|
||||
struct mtd_info *mtd = cxt->mtd;
|
||||
@ -201,7 +216,7 @@ static void mtdoops_write(struct mtdoops_context *cxt, int panic)
|
||||
ret = mtd_panic_write(mtd, cxt->nextpage * record_size,
|
||||
record_size, &retlen, cxt->oops_buf);
|
||||
if (ret == -EOPNOTSUPP) {
|
||||
printk(KERN_ERR "mtdoops: Cannot write from panic without panic_write\n");
|
||||
pr_err("Cannot write from panic without panic_write\n");
|
||||
goto out;
|
||||
}
|
||||
} else
|
||||
@ -209,12 +224,12 @@ static void mtdoops_write(struct mtdoops_context *cxt, int panic)
|
||||
record_size, &retlen, cxt->oops_buf);
|
||||
|
||||
if (retlen != record_size || ret < 0)
|
||||
printk(KERN_ERR "mtdoops: write failure at %ld (%td of %ld written), error %d\n",
|
||||
pr_err("write failure at %ld (%td of %ld written), error %d\n",
|
||||
cxt->nextpage * record_size, retlen, record_size, ret);
|
||||
mark_page_used(cxt, cxt->nextpage);
|
||||
memset(cxt->oops_buf, 0xff, record_size);
|
||||
|
||||
mtdoops_inc_counter(cxt);
|
||||
mtdoops_inc_counter(cxt, panic);
|
||||
out:
|
||||
clear_bit(0, &cxt->oops_buf_busy);
|
||||
}
|
||||
@ -244,7 +259,7 @@ static void find_next_position(struct mtdoops_context *cxt)
|
||||
&retlen, (u_char *)&hdr);
|
||||
if (retlen != sizeof(hdr) ||
|
||||
(ret < 0 && !mtd_is_bitflip(ret))) {
|
||||
printk(KERN_ERR "mtdoops: read failure at %ld (%zu of %zu read), err %d\n",
|
||||
pr_err("read failure at %ld (%zu of %zu read), err %d\n",
|
||||
page * record_size, retlen, sizeof(hdr), ret);
|
||||
continue;
|
||||
}
|
||||
@ -279,7 +294,7 @@ static void find_next_position(struct mtdoops_context *cxt)
|
||||
cxt->nextcount = maxcount;
|
||||
}
|
||||
|
||||
mtdoops_inc_counter(cxt);
|
||||
mtdoops_inc_counter(cxt, 0);
|
||||
}
|
||||
|
||||
static void mtdoops_do_dump(struct kmsg_dumper *dumper,
|
||||
@ -324,17 +339,17 @@ static void mtdoops_notify_add(struct mtd_info *mtd)
|
||||
return;
|
||||
|
||||
if (mtd->size < mtd->erasesize * 2) {
|
||||
printk(KERN_ERR "mtdoops: MTD partition %d not big enough for mtdoops\n",
|
||||
pr_err("MTD partition %d not big enough for mtdoops\n",
|
||||
mtd->index);
|
||||
return;
|
||||
}
|
||||
if (mtd->erasesize < record_size) {
|
||||
printk(KERN_ERR "mtdoops: eraseblock size of MTD partition %d too small\n",
|
||||
pr_err("eraseblock size of MTD partition %d too small\n",
|
||||
mtd->index);
|
||||
return;
|
||||
}
|
||||
if (mtd->size > MTDOOPS_MAX_MTD_SIZE) {
|
||||
printk(KERN_ERR "mtdoops: mtd%d is too large (limit is %d MiB)\n",
|
||||
pr_err("mtd%d is too large (limit is %d MiB)\n",
|
||||
mtd->index, MTDOOPS_MAX_MTD_SIZE / 1024 / 1024);
|
||||
return;
|
||||
}
|
||||
@ -345,7 +360,7 @@ static void mtdoops_notify_add(struct mtd_info *mtd)
|
||||
DIV_ROUND_UP(mtdoops_pages,
|
||||
BITS_PER_LONG)));
|
||||
if (!cxt->oops_page_used) {
|
||||
printk(KERN_ERR "mtdoops: could not allocate page array\n");
|
||||
pr_err("could not allocate page array\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -353,7 +368,7 @@ static void mtdoops_notify_add(struct mtd_info *mtd)
|
||||
cxt->dump.dump = mtdoops_do_dump;
|
||||
err = kmsg_dump_register(&cxt->dump);
|
||||
if (err) {
|
||||
printk(KERN_ERR "mtdoops: registering kmsg dumper failed, error %d\n", err);
|
||||
pr_err("registering kmsg dumper failed, error %d\n", err);
|
||||
vfree(cxt->oops_page_used);
|
||||
cxt->oops_page_used = NULL;
|
||||
return;
|
||||
@ -362,7 +377,7 @@ static void mtdoops_notify_add(struct mtd_info *mtd)
|
||||
cxt->mtd = mtd;
|
||||
cxt->oops_pages = (int)mtd->size / record_size;
|
||||
find_next_position(cxt);
|
||||
printk(KERN_INFO "mtdoops: Attached to MTD device %d\n", mtd->index);
|
||||
pr_info("Attached to MTD device %d\n", mtd->index);
|
||||
}
|
||||
|
||||
static void mtdoops_notify_remove(struct mtd_info *mtd)
|
||||
@ -373,7 +388,7 @@ static void mtdoops_notify_remove(struct mtd_info *mtd)
|
||||
return;
|
||||
|
||||
if (kmsg_dump_unregister(&cxt->dump) < 0)
|
||||
printk(KERN_WARNING "mtdoops: could not unregister kmsg_dumper\n");
|
||||
pr_warn("could not unregister kmsg_dumper\n");
|
||||
|
||||
cxt->mtd = NULL;
|
||||
flush_work(&cxt->work_erase);
|
||||
@ -393,15 +408,15 @@ static int __init mtdoops_init(void)
|
||||
char *endp;
|
||||
|
||||
if (strlen(mtddev) == 0) {
|
||||
printk(KERN_ERR "mtdoops: mtd device (mtddev=name/number) must be supplied\n");
|
||||
pr_err("mtd device (mtddev=name/number) must be supplied\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if ((record_size & 4095) != 0) {
|
||||
printk(KERN_ERR "mtdoops: record_size must be a multiple of 4096\n");
|
||||
pr_err("record_size must be a multiple of 4096\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (record_size < 4096) {
|
||||
printk(KERN_ERR "mtdoops: record_size must be over 4096 bytes\n");
|
||||
pr_err("record_size must be over 4096 bytes\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -126,7 +126,7 @@ EXPORT_SYMBOL_GPL(nanddev_isreserved);
|
||||
*
|
||||
* Return: 0 in case of success, a negative error code otherwise.
|
||||
*/
|
||||
int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos)
|
||||
static int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos)
|
||||
{
|
||||
if (nanddev_isbad(nand, pos) || nanddev_isreserved(nand, pos)) {
|
||||
pr_warn("attempt to erase a bad/reserved block @%llx\n",
|
||||
@ -136,7 +136,6 @@ int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos)
|
||||
|
||||
return nand->ops->erase(nand, pos);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nanddev_erase);
|
||||
|
||||
/**
|
||||
* nanddev_mtd_erase() - Generic mtd->_erase() implementation for NAND devices
|
||||
|
@ -415,7 +415,7 @@ config MTD_NAND_PLATFORM
|
||||
|
||||
config MTD_NAND_CADENCE
|
||||
tristate "Support Cadence NAND (HPNFC) controller"
|
||||
depends on (OF || COMPILE_TEST) && HAS_IOMEM
|
||||
depends on OF && HAS_IOMEM
|
||||
help
|
||||
Enable the driver for NAND flash on platforms using a Cadence NAND
|
||||
controller.
|
||||
@ -430,7 +430,7 @@ config MTD_NAND_ARASAN
|
||||
|
||||
config MTD_NAND_INTEL_LGM
|
||||
tristate "Support for NAND controller on Intel LGM SoC"
|
||||
depends on OF || COMPILE_TEST
|
||||
depends on OF
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables support for NAND Flash chips on Intel's LGM SoC.
|
||||
@ -450,7 +450,7 @@ config MTD_NAND_ROCKCHIP
|
||||
|
||||
config MTD_NAND_PL35X
|
||||
tristate "ARM PL35X NAND controller"
|
||||
depends on OF || COMPILE_TEST
|
||||
depends on OF
|
||||
depends on PL353_SMC
|
||||
help
|
||||
Enables support for PrimeCell SMC PL351 and PL353 NAND
|
||||
|
@ -1184,6 +1184,14 @@ static int cadence_nand_hw_init(struct cdns_nand_ctrl *cdns_ctrl)
|
||||
if (cadence_nand_read_bch_caps(cdns_ctrl))
|
||||
return -EIO;
|
||||
|
||||
#ifndef CONFIG_64BIT
|
||||
if (cdns_ctrl->caps2.data_dma_width == 8) {
|
||||
dev_err(cdns_ctrl->dev,
|
||||
"cannot access 64-bit dma on !64-bit architectures");
|
||||
return -EIO;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Set IO width access to 8.
|
||||
* It is because during SW device discovering width access
|
||||
@ -1882,17 +1890,36 @@ static int cadence_nand_read_buf(struct cdns_nand_ctrl *cdns_ctrl,
|
||||
return status;
|
||||
|
||||
if (!cdns_ctrl->caps1->has_dma) {
|
||||
int len_in_words = len >> 2;
|
||||
u8 data_dma_width = cdns_ctrl->caps2.data_dma_width;
|
||||
|
||||
int len_in_words = (data_dma_width == 4) ? len >> 2 : len >> 3;
|
||||
|
||||
/* read alingment data */
|
||||
ioread32_rep(cdns_ctrl->io.virt, buf, len_in_words);
|
||||
if (data_dma_width == 4)
|
||||
ioread32_rep(cdns_ctrl->io.virt, buf, len_in_words);
|
||||
#ifdef CONFIG_64BIT
|
||||
else
|
||||
readsq(cdns_ctrl->io.virt, buf, len_in_words);
|
||||
#endif
|
||||
|
||||
if (sdma_size > len) {
|
||||
int read_bytes = (data_dma_width == 4) ?
|
||||
len_in_words << 2 : len_in_words << 3;
|
||||
|
||||
/* read rest data from slave DMA interface if any */
|
||||
ioread32_rep(cdns_ctrl->io.virt, cdns_ctrl->buf,
|
||||
sdma_size / 4 - len_in_words);
|
||||
if (data_dma_width == 4)
|
||||
ioread32_rep(cdns_ctrl->io.virt,
|
||||
cdns_ctrl->buf,
|
||||
sdma_size / 4 - len_in_words);
|
||||
#ifdef CONFIG_64BIT
|
||||
else
|
||||
readsq(cdns_ctrl->io.virt, cdns_ctrl->buf,
|
||||
sdma_size / 8 - len_in_words);
|
||||
#endif
|
||||
|
||||
/* copy rest of data */
|
||||
memcpy(buf + (len_in_words << 2), cdns_ctrl->buf,
|
||||
len - (len_in_words << 2));
|
||||
memcpy(buf + read_bytes, cdns_ctrl->buf,
|
||||
len - read_bytes);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1936,16 +1963,35 @@ static int cadence_nand_write_buf(struct cdns_nand_ctrl *cdns_ctrl,
|
||||
return status;
|
||||
|
||||
if (!cdns_ctrl->caps1->has_dma) {
|
||||
int len_in_words = len >> 2;
|
||||
u8 data_dma_width = cdns_ctrl->caps2.data_dma_width;
|
||||
|
||||
int len_in_words = (data_dma_width == 4) ? len >> 2 : len >> 3;
|
||||
|
||||
if (data_dma_width == 4)
|
||||
iowrite32_rep(cdns_ctrl->io.virt, buf, len_in_words);
|
||||
#ifdef CONFIG_64BIT
|
||||
else
|
||||
writesq(cdns_ctrl->io.virt, buf, len_in_words);
|
||||
#endif
|
||||
|
||||
iowrite32_rep(cdns_ctrl->io.virt, buf, len_in_words);
|
||||
if (sdma_size > len) {
|
||||
int written_bytes = (data_dma_width == 4) ?
|
||||
len_in_words << 2 : len_in_words << 3;
|
||||
|
||||
/* copy rest of data */
|
||||
memcpy(cdns_ctrl->buf, buf + (len_in_words << 2),
|
||||
len - (len_in_words << 2));
|
||||
memcpy(cdns_ctrl->buf, buf + written_bytes,
|
||||
len - written_bytes);
|
||||
|
||||
/* write all expected by nand controller data */
|
||||
iowrite32_rep(cdns_ctrl->io.virt, cdns_ctrl->buf,
|
||||
sdma_size / 4 - len_in_words);
|
||||
if (data_dma_width == 4)
|
||||
iowrite32_rep(cdns_ctrl->io.virt,
|
||||
cdns_ctrl->buf,
|
||||
sdma_size / 4 - len_in_words);
|
||||
#ifdef CONFIG_64BIT
|
||||
else
|
||||
writesq(cdns_ctrl->io.virt, cdns_ctrl->buf,
|
||||
sdma_size / 8 - len_in_words);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -148,11 +148,9 @@ static int gpmi_init(struct gpmi_nand_data *this)
|
||||
struct resources *r = &this->resources;
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(this->dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(this->dev);
|
||||
ret = pm_runtime_resume_and_get(this->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gpmi_reset_block(r->gpmi_regs, false);
|
||||
if (ret)
|
||||
@ -2504,11 +2502,9 @@ static int gpmi_nfc_exec_op(struct nand_chip *chip,
|
||||
for (i = 0; i < GPMI_MAX_TRANSFERS; i++)
|
||||
this->transfers[i].direction = DMA_NONE;
|
||||
|
||||
ret = pm_runtime_get_sync(this->dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(this->dev);
|
||||
ret = pm_runtime_resume_and_get(this->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* This driver currently supports only one NAND chip. Plus, dies share
|
||||
|
@ -25,7 +25,7 @@
|
||||
#include <linux/completion.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/mtd/lpc32xx_mlc.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mm.h>
|
||||
@ -122,7 +122,6 @@ struct lpc32xx_nand_cfg_mlc {
|
||||
uint32_t rd_low;
|
||||
uint32_t wr_high;
|
||||
uint32_t wr_low;
|
||||
int wp_gpio;
|
||||
struct mtd_partition *parts;
|
||||
unsigned num_parts;
|
||||
};
|
||||
@ -177,6 +176,7 @@ struct lpc32xx_nand_host {
|
||||
struct nand_chip nand_chip;
|
||||
struct lpc32xx_mlc_platform_data *pdata;
|
||||
struct clk *clk;
|
||||
struct gpio_desc *wp_gpio;
|
||||
void __iomem *io_base;
|
||||
int irq;
|
||||
struct lpc32xx_nand_cfg_mlc *ncfg;
|
||||
@ -370,8 +370,8 @@ static int lpc32xx_waitfunc(struct nand_chip *chip)
|
||||
*/
|
||||
static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host)
|
||||
{
|
||||
if (gpio_is_valid(host->ncfg->wp_gpio))
|
||||
gpio_set_value(host->ncfg->wp_gpio, 0);
|
||||
if (host->wp_gpio)
|
||||
gpiod_set_value_cansleep(host->wp_gpio, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -379,8 +379,8 @@ static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host)
|
||||
*/
|
||||
static void lpc32xx_wp_disable(struct lpc32xx_nand_host *host)
|
||||
{
|
||||
if (gpio_is_valid(host->ncfg->wp_gpio))
|
||||
gpio_set_value(host->ncfg->wp_gpio, 1);
|
||||
if (host->wp_gpio)
|
||||
gpiod_set_value_cansleep(host->wp_gpio, 0);
|
||||
}
|
||||
|
||||
static void lpc32xx_dma_complete_func(void *completion)
|
||||
@ -636,8 +636,6 @@ static struct lpc32xx_nand_cfg_mlc *lpc32xx_parse_dt(struct device *dev)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ncfg->wp_gpio = of_get_named_gpio(np, "gpios", 0);
|
||||
|
||||
return ncfg;
|
||||
}
|
||||
|
||||
@ -713,14 +711,18 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
|
||||
"Missing or bad NAND config from device tree\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
if (host->ncfg->wp_gpio == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
if (gpio_is_valid(host->ncfg->wp_gpio) &&
|
||||
gpio_request(host->ncfg->wp_gpio, "NAND WP")) {
|
||||
dev_err(&pdev->dev, "GPIO not available\n");
|
||||
return -EBUSY;
|
||||
|
||||
/* Start with WP disabled, if available */
|
||||
host->wp_gpio = gpiod_get_optional(&pdev->dev, NULL, GPIOD_OUT_LOW);
|
||||
res = PTR_ERR_OR_ZERO(host->wp_gpio);
|
||||
if (res) {
|
||||
if (res != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "WP GPIO is not available: %d\n",
|
||||
res);
|
||||
return res;
|
||||
}
|
||||
lpc32xx_wp_disable(host);
|
||||
|
||||
gpiod_set_consumer_name(host->wp_gpio, "NAND WP");
|
||||
|
||||
host->pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
@ -817,7 +819,7 @@ put_clk:
|
||||
clk_put(host->clk);
|
||||
free_gpio:
|
||||
lpc32xx_wp_enable(host);
|
||||
gpio_free(host->ncfg->wp_gpio);
|
||||
gpiod_put(host->wp_gpio);
|
||||
|
||||
return res;
|
||||
}
|
||||
@ -843,12 +845,11 @@ static int lpc32xx_nand_remove(struct platform_device *pdev)
|
||||
clk_put(host->clk);
|
||||
|
||||
lpc32xx_wp_enable(host);
|
||||
gpio_free(host->ncfg->wp_gpio);
|
||||
gpiod_put(host->wp_gpio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int lpc32xx_nand_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct lpc32xx_nand_host *host = platform_get_drvdata(pdev);
|
||||
@ -880,11 +881,6 @@ static int lpc32xx_nand_suspend(struct platform_device *pdev, pm_message_t pm)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
#define lpc32xx_nand_resume NULL
|
||||
#define lpc32xx_nand_suspend NULL
|
||||
#endif
|
||||
|
||||
static const struct of_device_id lpc32xx_nand_match[] = {
|
||||
{ .compatible = "nxp,lpc3220-mlc" },
|
||||
{ /* sentinel */ },
|
||||
@ -894,8 +890,8 @@ MODULE_DEVICE_TABLE(of, lpc32xx_nand_match);
|
||||
static struct platform_driver lpc32xx_nand_driver = {
|
||||
.probe = lpc32xx_nand_probe,
|
||||
.remove = lpc32xx_nand_remove,
|
||||
.resume = lpc32xx_nand_resume,
|
||||
.suspend = lpc32xx_nand_suspend,
|
||||
.resume = pm_ptr(lpc32xx_nand_resume),
|
||||
.suspend = pm_ptr(lpc32xx_nand_suspend),
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.of_match_table = lpc32xx_nand_match,
|
||||
|
@ -23,9 +23,8 @@
|
||||
#include <linux/mm.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/mtd/lpc32xx_slc.h>
|
||||
|
||||
#define LPC32XX_MODNAME "lpc32xx-nand"
|
||||
@ -208,7 +207,6 @@ struct lpc32xx_nand_cfg_slc {
|
||||
uint32_t rwidth;
|
||||
uint32_t rhold;
|
||||
uint32_t rsetup;
|
||||
int wp_gpio;
|
||||
struct mtd_partition *parts;
|
||||
unsigned num_parts;
|
||||
};
|
||||
@ -217,6 +215,7 @@ struct lpc32xx_nand_host {
|
||||
struct nand_chip nand_chip;
|
||||
struct lpc32xx_slc_platform_data *pdata;
|
||||
struct clk *clk;
|
||||
struct gpio_desc *wp_gpio;
|
||||
void __iomem *io_base;
|
||||
struct lpc32xx_nand_cfg_slc *ncfg;
|
||||
|
||||
@ -309,8 +308,8 @@ static int lpc32xx_nand_device_ready(struct nand_chip *chip)
|
||||
*/
|
||||
static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host)
|
||||
{
|
||||
if (gpio_is_valid(host->ncfg->wp_gpio))
|
||||
gpio_set_value(host->ncfg->wp_gpio, 0);
|
||||
if (host->wp_gpio)
|
||||
gpiod_set_value_cansleep(host->wp_gpio, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -318,8 +317,8 @@ static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host)
|
||||
*/
|
||||
static void lpc32xx_wp_disable(struct lpc32xx_nand_host *host)
|
||||
{
|
||||
if (gpio_is_valid(host->ncfg->wp_gpio))
|
||||
gpio_set_value(host->ncfg->wp_gpio, 1);
|
||||
if (host->wp_gpio)
|
||||
gpiod_set_value_cansleep(host->wp_gpio, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -764,8 +763,6 @@ static struct lpc32xx_nand_cfg_slc *lpc32xx_parse_dt(struct device *dev)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ncfg->wp_gpio = of_get_named_gpio(np, "gpios", 0);
|
||||
|
||||
return ncfg;
|
||||
}
|
||||
|
||||
@ -852,14 +849,18 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
|
||||
"Missing or bad NAND config from device tree\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
if (host->ncfg->wp_gpio == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
if (gpio_is_valid(host->ncfg->wp_gpio) && devm_gpio_request(&pdev->dev,
|
||||
host->ncfg->wp_gpio, "NAND WP")) {
|
||||
dev_err(&pdev->dev, "GPIO not available\n");
|
||||
return -EBUSY;
|
||||
|
||||
/* Start with WP disabled, if available */
|
||||
host->wp_gpio = gpiod_get_optional(&pdev->dev, NULL, GPIOD_OUT_LOW);
|
||||
res = PTR_ERR_OR_ZERO(host->wp_gpio);
|
||||
if (res) {
|
||||
if (res != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "WP GPIO is not available: %d\n",
|
||||
res);
|
||||
return res;
|
||||
}
|
||||
lpc32xx_wp_disable(host);
|
||||
|
||||
gpiod_set_consumer_name(host->wp_gpio, "NAND WP");
|
||||
|
||||
host->pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
@ -968,7 +969,6 @@ static int lpc32xx_nand_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int lpc32xx_nand_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct lpc32xx_nand_host *host = platform_get_drvdata(pdev);
|
||||
@ -1007,11 +1007,6 @@ static int lpc32xx_nand_suspend(struct platform_device *pdev, pm_message_t pm)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
#define lpc32xx_nand_resume NULL
|
||||
#define lpc32xx_nand_suspend NULL
|
||||
#endif
|
||||
|
||||
static const struct of_device_id lpc32xx_nand_match[] = {
|
||||
{ .compatible = "nxp,lpc3220-slc" },
|
||||
{ /* sentinel */ },
|
||||
@ -1021,8 +1016,8 @@ MODULE_DEVICE_TABLE(of, lpc32xx_nand_match);
|
||||
static struct platform_driver lpc32xx_nand_driver = {
|
||||
.probe = lpc32xx_nand_probe,
|
||||
.remove = lpc32xx_nand_remove,
|
||||
.resume = lpc32xx_nand_resume,
|
||||
.suspend = lpc32xx_nand_suspend,
|
||||
.resume = pm_ptr(lpc32xx_nand_resume),
|
||||
.suspend = pm_ptr(lpc32xx_nand_suspend),
|
||||
.driver = {
|
||||
.name = LPC32XX_MODNAME,
|
||||
.of_match_table = lpc32xx_nand_match,
|
||||
|
@ -114,6 +114,7 @@
|
||||
#define GENCONF_SOC_DEVICE_MUX_ECC_CLK_RST BIT(20)
|
||||
#define GENCONF_SOC_DEVICE_MUX_ECC_CORE_RST BIT(21)
|
||||
#define GENCONF_SOC_DEVICE_MUX_NFC_INT_EN BIT(25)
|
||||
#define GENCONF_SOC_DEVICE_MUX_NFC_DEVBUS_ARB_EN BIT(27)
|
||||
#define GENCONF_CLK_GATING_CTRL 0x220
|
||||
#define GENCONF_CLK_GATING_CTRL_ND_GATE BIT(2)
|
||||
#define GENCONF_ND_CLK_CTRL 0x700
|
||||
@ -2880,7 +2881,8 @@ static int marvell_nfc_init(struct marvell_nfc *nfc)
|
||||
GENCONF_SOC_DEVICE_MUX_NFC_EN |
|
||||
GENCONF_SOC_DEVICE_MUX_ECC_CLK_RST |
|
||||
GENCONF_SOC_DEVICE_MUX_ECC_CORE_RST |
|
||||
GENCONF_SOC_DEVICE_MUX_NFC_INT_EN);
|
||||
GENCONF_SOC_DEVICE_MUX_NFC_INT_EN |
|
||||
GENCONF_SOC_DEVICE_MUX_NFC_DEVBUS_ARB_EN);
|
||||
|
||||
regmap_update_bits(sysctrl_base, GENCONF_CLK_GATING_CTRL,
|
||||
GENCONF_CLK_GATING_CTRL_ND_GATE,
|
||||
|
@ -663,7 +663,7 @@ static int mpc5121_nfc_probe(struct platform_device *op)
|
||||
}
|
||||
|
||||
prv->irq = irq_of_parse_and_map(dn, 0);
|
||||
if (prv->irq == NO_IRQ) {
|
||||
if (!prv->irq) {
|
||||
dev_err(dev, "Error mapping IRQ!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -74,9 +74,75 @@ static int w25m02gv_select_target(struct spinand_device *spinand,
|
||||
return spi_mem_exec_op(spinand->spimem, &op);
|
||||
}
|
||||
|
||||
static int w25n02kv_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *region)
|
||||
{
|
||||
if (section > 3)
|
||||
return -ERANGE;
|
||||
|
||||
region->offset = 64 + (16 * section);
|
||||
region->length = 13;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int w25n02kv_ooblayout_free(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *region)
|
||||
{
|
||||
if (section > 3)
|
||||
return -ERANGE;
|
||||
|
||||
region->offset = (16 * section) + 2;
|
||||
region->length = 14;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct mtd_ooblayout_ops w25n02kv_ooblayout = {
|
||||
.ecc = w25n02kv_ooblayout_ecc,
|
||||
.free = w25n02kv_ooblayout_free,
|
||||
};
|
||||
|
||||
static int w25n02kv_ecc_get_status(struct spinand_device *spinand,
|
||||
u8 status)
|
||||
{
|
||||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
u8 mbf = 0;
|
||||
struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, &mbf);
|
||||
|
||||
switch (status & STATUS_ECC_MASK) {
|
||||
case STATUS_ECC_NO_BITFLIPS:
|
||||
return 0;
|
||||
|
||||
case STATUS_ECC_UNCOR_ERROR:
|
||||
return -EBADMSG;
|
||||
|
||||
case STATUS_ECC_HAS_BITFLIPS:
|
||||
/*
|
||||
* Let's try to retrieve the real maximum number of bitflips
|
||||
* in order to avoid forcing the wear-leveling layer to move
|
||||
* data around if it's not necessary.
|
||||
*/
|
||||
if (spi_mem_exec_op(spinand->spimem, &op))
|
||||
return nanddev_get_ecc_conf(nand)->strength;
|
||||
|
||||
mbf >>= 4;
|
||||
|
||||
if (WARN_ON(mbf > nanddev_get_ecc_conf(nand)->strength || !mbf))
|
||||
return nanddev_get_ecc_conf(nand)->strength;
|
||||
|
||||
return mbf;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct spinand_info winbond_spinand_table[] = {
|
||||
SPINAND_INFO("W25M02GV",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab),
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21),
|
||||
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 2),
|
||||
NAND_ECCREQ(1, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
@ -86,7 +152,7 @@ static const struct spinand_info winbond_spinand_table[] = {
|
||||
SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
|
||||
SPINAND_SELECT_TARGET(w25m02gv_select_target)),
|
||||
SPINAND_INFO("W25N01GV",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa),
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x21),
|
||||
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
|
||||
NAND_ECCREQ(1, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
@ -94,6 +160,15 @@ static const struct spinand_info winbond_spinand_table[] = {
|
||||
&update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)),
|
||||
SPINAND_INFO("W25N02KV",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x22),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
|
||||
};
|
||||
|
||||
static int winbond_spinand_init(struct spinand_device *spinand)
|
||||
|
@ -22,7 +22,7 @@ config MTD_BCM63XX_PARTS
|
||||
|
||||
config MTD_BRCM_U_BOOT
|
||||
tristate "Broadcom's U-Boot partition parser"
|
||||
depends on ARCH_BCM4908 || COMPILE_TEST
|
||||
depends on ARCH_BCMBCA || COMPILE_TEST
|
||||
help
|
||||
Broadcom uses a custom way of storing U-Boot environment variables.
|
||||
They are placed inside U-Boot partition itself at unspecified offset.
|
||||
@ -75,7 +75,7 @@ config MTD_OF_PARTS
|
||||
This provides a open firmware device tree partition parser
|
||||
which derives the partition map from the children of the
|
||||
flash memory node, as described in
|
||||
Documentation/devicetree/bindings/mtd/partition.txt.
|
||||
Documentation/devicetree/bindings/mtd/mtd.yaml.
|
||||
|
||||
config MTD_OF_PARTS_BCM4908
|
||||
bool "BCM4908 partitioning support"
|
||||
@ -123,6 +123,21 @@ config MTD_AFS_PARTS
|
||||
for your particular device. It won't happen automatically. The
|
||||
'physmap' map driver (CONFIG_MTD_PHYSMAP) does this, for example.
|
||||
|
||||
config MTD_PARSER_TPLINK_SAFELOADER
|
||||
tristate "TP-Link Safeloader partitions parser"
|
||||
depends on MTD && (ARCH_BCM_5301X || ATH79 || SOC_MT7620 || SOC_MT7621 || COMPILE_TEST)
|
||||
help
|
||||
TP-Link home routers use flash partitions to store various data. Info
|
||||
about flash space layout is stored in a partitions table using a
|
||||
custom ASCII-based format.
|
||||
|
||||
That format was first found in devices with SafeLoader bootloader and
|
||||
was named after it. Later it was adapted to CFE and U-Boot
|
||||
bootloaders.
|
||||
|
||||
This driver reads partitions table, parses it and creates MTD
|
||||
partitions.
|
||||
|
||||
config MTD_PARSER_TRX
|
||||
tristate "Parser for TRX format partitions"
|
||||
depends on MTD && (BCM47XX || ARCH_BCM_5301X || ARCH_MEDIATEK || RALINK || COMPILE_TEST)
|
||||
|
@ -10,6 +10,7 @@ ofpart-$(CONFIG_MTD_OF_PARTS_BCM4908) += ofpart_bcm4908.o
|
||||
ofpart-$(CONFIG_MTD_OF_PARTS_LINKSYS_NS)+= ofpart_linksys_ns.o
|
||||
obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o
|
||||
obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
|
||||
obj-$(CONFIG_MTD_PARSER_TPLINK_SAFELOADER) += tplink_safeloader.o
|
||||
obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o
|
||||
obj-$(CONFIG_MTD_SERCOMM_PARTS) += scpart.o
|
||||
obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o
|
||||
|
150
drivers/mtd/parsers/tplink_safeloader.c
Normal file
150
drivers/mtd/parsers/tplink_safeloader.c
Normal file
@ -0,0 +1,150 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright © 2022 Rafał Miłecki <rafal@milecki.pl>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define TPLINK_SAFELOADER_DATA_OFFSET 4
|
||||
#define TPLINK_SAFELOADER_MAX_PARTS 32
|
||||
|
||||
struct safeloader_cmn_header {
|
||||
__be32 size;
|
||||
uint32_t unused;
|
||||
} __packed;
|
||||
|
||||
static void *mtd_parser_tplink_safeloader_read_table(struct mtd_info *mtd)
|
||||
{
|
||||
struct safeloader_cmn_header hdr;
|
||||
struct device_node *np;
|
||||
size_t bytes_read;
|
||||
size_t size;
|
||||
u32 offset;
|
||||
char *buf;
|
||||
int err;
|
||||
|
||||
np = mtd_get_of_node(mtd);
|
||||
if (mtd_is_partition(mtd))
|
||||
of_node_get(np);
|
||||
else
|
||||
np = of_get_child_by_name(np, "partitions");
|
||||
|
||||
if (of_property_read_u32(np, "partitions-table-offset", &offset)) {
|
||||
pr_err("Failed to get partitions table offset\n");
|
||||
goto err_put;
|
||||
}
|
||||
|
||||
err = mtd_read(mtd, offset, sizeof(hdr), &bytes_read, (uint8_t *)&hdr);
|
||||
if (err && !mtd_is_bitflip(err)) {
|
||||
pr_err("Failed to read from %s at 0x%x\n", mtd->name, offset);
|
||||
goto err_put;
|
||||
}
|
||||
|
||||
size = be32_to_cpu(hdr.size);
|
||||
|
||||
buf = kmalloc(size + 1, GFP_KERNEL);
|
||||
if (!buf)
|
||||
goto err_put;
|
||||
|
||||
err = mtd_read(mtd, offset + sizeof(hdr), size, &bytes_read, buf);
|
||||
if (err && !mtd_is_bitflip(err)) {
|
||||
pr_err("Failed to read from %s at 0x%zx\n", mtd->name, offset + sizeof(hdr));
|
||||
goto err_kfree;
|
||||
}
|
||||
|
||||
buf[size] = '\0';
|
||||
|
||||
of_node_put(np);
|
||||
|
||||
return buf;
|
||||
|
||||
err_kfree:
|
||||
kfree(buf);
|
||||
err_put:
|
||||
of_node_put(np);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int mtd_parser_tplink_safeloader_parse(struct mtd_info *mtd,
|
||||
const struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
struct mtd_partition *parts;
|
||||
char name[65];
|
||||
size_t offset;
|
||||
size_t bytes;
|
||||
char *buf;
|
||||
int idx;
|
||||
int err;
|
||||
|
||||
parts = kcalloc(TPLINK_SAFELOADER_MAX_PARTS, sizeof(*parts), GFP_KERNEL);
|
||||
if (!parts) {
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
buf = mtd_parser_tplink_safeloader_read_table(mtd);
|
||||
if (!buf) {
|
||||
err = -ENOENT;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
for (idx = 0, offset = TPLINK_SAFELOADER_DATA_OFFSET;
|
||||
idx < TPLINK_SAFELOADER_MAX_PARTS &&
|
||||
sscanf(buf + offset, "partition %64s base 0x%llx size 0x%llx%zn\n",
|
||||
name, &parts[idx].offset, &parts[idx].size, &bytes) == 3;
|
||||
idx++, offset += bytes + 1) {
|
||||
parts[idx].name = kstrdup(name, GFP_KERNEL);
|
||||
if (!parts[idx].name) {
|
||||
err = -ENOMEM;
|
||||
goto err_free;
|
||||
}
|
||||
}
|
||||
|
||||
if (idx == TPLINK_SAFELOADER_MAX_PARTS)
|
||||
pr_warn("Reached maximum number of partitions!\n");
|
||||
|
||||
kfree(buf);
|
||||
|
||||
*pparts = parts;
|
||||
|
||||
return idx;
|
||||
|
||||
err_free:
|
||||
for (idx -= 1; idx >= 0; idx--)
|
||||
kfree(parts[idx].name);
|
||||
err_out:
|
||||
return err;
|
||||
};
|
||||
|
||||
static void mtd_parser_tplink_safeloader_cleanup(const struct mtd_partition *pparts,
|
||||
int nr_parts)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr_parts; i++)
|
||||
kfree(pparts[i].name);
|
||||
|
||||
kfree(pparts);
|
||||
}
|
||||
|
||||
static const struct of_device_id mtd_parser_tplink_safeloader_of_match_table[] = {
|
||||
{ .compatible = "tplink,safeloader-partitions" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mtd_parser_tplink_safeloader_of_match_table);
|
||||
|
||||
static struct mtd_part_parser mtd_parser_tplink_safeloader = {
|
||||
.parse_fn = mtd_parser_tplink_safeloader_parse,
|
||||
.cleanup = mtd_parser_tplink_safeloader_cleanup,
|
||||
.name = "tplink-safeloader",
|
||||
.of_match_table = mtd_parser_tplink_safeloader_of_match_table,
|
||||
};
|
||||
module_mtd_part_parser(mtd_parser_tplink_safeloader);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
@ -1184,6 +1184,8 @@ spi_nor_find_best_erase_type(const struct spi_nor_erase_map *map,
|
||||
continue;
|
||||
|
||||
erase = &map->erase_type[i];
|
||||
if (!erase->size)
|
||||
continue;
|
||||
|
||||
/* Alignment is not mandatory for overlaid regions */
|
||||
if (region->offset & SNOR_OVERLAID_REGION &&
|
||||
@ -1632,6 +1634,16 @@ static const struct spi_nor_manufacturer *manufacturers[] = {
|
||||
&spi_nor_xmc,
|
||||
};
|
||||
|
||||
static const struct flash_info spi_nor_generic_flash = {
|
||||
.name = "spi-nor-generic",
|
||||
/*
|
||||
* JESD216 rev A doesn't specify the page size, therefore we need a
|
||||
* sane default.
|
||||
*/
|
||||
.page_size = 256,
|
||||
.parse_sfdp = true,
|
||||
};
|
||||
|
||||
static const struct flash_info *spi_nor_match_id(struct spi_nor *nor,
|
||||
const u8 *id)
|
||||
{
|
||||
@ -1664,7 +1676,20 @@ static const struct flash_info *spi_nor_detect(struct spi_nor *nor)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/* Cache the complete flash ID. */
|
||||
nor->id = devm_kmemdup(nor->dev, id, SPI_NOR_MAX_ID_LEN, GFP_KERNEL);
|
||||
if (!nor->id)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
info = spi_nor_match_id(nor, id);
|
||||
|
||||
/* Fallback to a generic flash described only by its SFDP data. */
|
||||
if (!info) {
|
||||
ret = spi_nor_check_sfdp_signature(nor);
|
||||
if (!ret)
|
||||
info = &spi_nor_generic_flash;
|
||||
}
|
||||
|
||||
if (!info) {
|
||||
dev_err(nor->dev, "unrecognized JEDEC id bytes: %*ph\n",
|
||||
SPI_NOR_MAX_ID_LEN, id);
|
||||
@ -1914,7 +1939,8 @@ static int spi_nor_spimem_check_readop(struct spi_nor *nor,
|
||||
spi_nor_spimem_setup_op(nor, &op, read->proto);
|
||||
|
||||
/* convert the dummy cycles to the number of bytes */
|
||||
op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8;
|
||||
op.dummy.nbytes = (read->num_mode_clocks + read->num_wait_states) *
|
||||
op.dummy.buswidth / 8;
|
||||
if (spi_nor_protocol_is_dtr(nor->read_proto))
|
||||
op.dummy.nbytes *= 2;
|
||||
|
||||
@ -2091,8 +2117,12 @@ static int spi_nor_select_pp(struct spi_nor *nor,
|
||||
* spi_nor_select_uniform_erase() - select optimum uniform erase type
|
||||
* @map: the erase map of the SPI NOR
|
||||
* @wanted_size: the erase type size to search for. Contains the value of
|
||||
* info->sector_size or of the "small sector" size in case
|
||||
* CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is defined.
|
||||
* info->sector_size, the "small sector" size in case
|
||||
* CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is defined or 0 if
|
||||
* there is no information about the sector size. The
|
||||
* latter is the case if the flash parameters are parsed
|
||||
* solely by SFDP, then the largest supported erase type
|
||||
* is selected.
|
||||
*
|
||||
* Once the optimum uniform sector erase command is found, disable all the
|
||||
* other.
|
||||
@ -2113,6 +2143,10 @@ spi_nor_select_uniform_erase(struct spi_nor_erase_map *map,
|
||||
|
||||
tested_erase = &map->erase_type[i];
|
||||
|
||||
/* Skip masked erase types. */
|
||||
if (!tested_erase->size)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If the current erase size is the one, stop here:
|
||||
* we have found the right uniform Sector Erase command.
|
||||
@ -2565,6 +2599,12 @@ static void spi_nor_init_default_params(struct spi_nor *nor)
|
||||
params->hwcaps.mask |= SNOR_HWCAPS_PP;
|
||||
spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP],
|
||||
SPINOR_OP_PP, SNOR_PROTO_1_1_1);
|
||||
|
||||
if (info->flags & SPI_NOR_QUAD_PP) {
|
||||
params->hwcaps.mask |= SNOR_HWCAPS_PP_1_1_4;
|
||||
spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP_1_1_4],
|
||||
SPINOR_OP_PP_1_1_4, SNOR_PROTO_1_1_4);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2840,10 +2880,20 @@ static void spi_nor_put_device(struct mtd_info *mtd)
|
||||
|
||||
void spi_nor_restore(struct spi_nor *nor)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* restore the addressing mode */
|
||||
if (nor->addr_nbytes == 4 && !(nor->flags & SNOR_F_4B_OPCODES) &&
|
||||
nor->flags & SNOR_F_BROKEN_RESET)
|
||||
nor->params->set_4byte_addr_mode(nor, false);
|
||||
nor->flags & SNOR_F_BROKEN_RESET) {
|
||||
ret = nor->params->set_4byte_addr_mode(nor, false);
|
||||
if (ret)
|
||||
/*
|
||||
* Do not stop the execution in the hope that the flash
|
||||
* will default to the 3-byte address mode after the
|
||||
* software reset.
|
||||
*/
|
||||
dev_err(nor->dev, "Failed to exit 4-byte address mode, err = %d\n", ret);
|
||||
}
|
||||
|
||||
if (nor->flags & SNOR_F_SOFT_RESET)
|
||||
spi_nor_soft_reset(nor);
|
||||
@ -2935,6 +2985,27 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
|
||||
mtd->_put_device = spi_nor_put_device;
|
||||
}
|
||||
|
||||
static int spi_nor_hw_reset(struct spi_nor *nor)
|
||||
{
|
||||
struct gpio_desc *reset;
|
||||
|
||||
reset = devm_gpiod_get_optional(nor->dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR_OR_NULL(reset))
|
||||
return PTR_ERR_OR_ZERO(reset);
|
||||
|
||||
/*
|
||||
* Experimental delay values by looking at different flash device
|
||||
* vendors datasheets.
|
||||
*/
|
||||
usleep_range(1, 5);
|
||||
gpiod_set_value_cansleep(reset, 1);
|
||||
usleep_range(100, 150);
|
||||
gpiod_set_value_cansleep(reset, 0);
|
||||
usleep_range(1000, 1200);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spi_nor_scan(struct spi_nor *nor, const char *name,
|
||||
const struct spi_nor_hwcaps *hwcaps)
|
||||
{
|
||||
@ -2967,6 +3038,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
|
||||
if (!nor->bouncebuf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = spi_nor_hw_reset(nor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
info = spi_nor_get_flash_info(nor, name);
|
||||
if (IS_ERR(info))
|
||||
return PTR_ERR(info);
|
||||
|
@ -458,6 +458,7 @@ struct spi_nor_fixups {
|
||||
* SPI_NOR_NO_ERASE: no erase command needed.
|
||||
* NO_CHIP_ERASE: chip does not support chip erase.
|
||||
* SPI_NOR_NO_FR: can't do fastread.
|
||||
* SPI_NOR_QUAD_PP: flash supports Quad Input Page Program.
|
||||
*
|
||||
* @no_sfdp_flags: flags that indicate support that can be discovered via SFDP.
|
||||
* Used when SFDP tables are not defined in the flash. These
|
||||
@ -507,6 +508,7 @@ struct flash_info {
|
||||
#define SPI_NOR_NO_ERASE BIT(6)
|
||||
#define NO_CHIP_ERASE BIT(7)
|
||||
#define SPI_NOR_NO_FR BIT(8)
|
||||
#define SPI_NOR_QUAD_PP BIT(9)
|
||||
|
||||
u8 no_sfdp_flags;
|
||||
#define SPI_NOR_SKIP_SFDP BIT(0)
|
||||
@ -701,6 +703,9 @@ int spi_nor_controller_ops_read_reg(struct spi_nor *nor, u8 opcode,
|
||||
int spi_nor_controller_ops_write_reg(struct spi_nor *nor, u8 opcode,
|
||||
const u8 *buf, size_t len);
|
||||
|
||||
int spi_nor_check_sfdp_signature(struct spi_nor *nor);
|
||||
int spi_nor_parse_sfdp(struct spi_nor *nor);
|
||||
|
||||
static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
|
||||
{
|
||||
return container_of(mtd, struct spi_nor, mtd);
|
||||
|
@ -81,7 +81,7 @@ static int spi_nor_params_show(struct seq_file *s, void *data)
|
||||
int i;
|
||||
|
||||
seq_printf(s, "name\t\t%s\n", info->name);
|
||||
seq_printf(s, "id\t\t%*ph\n", info->id_len, info->id);
|
||||
seq_printf(s, "id\t\t%*ph\n", SPI_NOR_MAX_ID_LEN, nor->id);
|
||||
string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf));
|
||||
seq_printf(s, "size\t\t%s\n", buf);
|
||||
seq_printf(s, "write size\t%u\n", params->writesize);
|
||||
|
@ -8,19 +8,29 @@
|
||||
|
||||
#include "core.h"
|
||||
|
||||
static void gd25q256_default_init(struct spi_nor *nor)
|
||||
static int
|
||||
gd25q256_post_bfpt(struct spi_nor *nor,
|
||||
const struct sfdp_parameter_header *bfpt_header,
|
||||
const struct sfdp_bfpt *bfpt)
|
||||
{
|
||||
/*
|
||||
* Some manufacturer like GigaDevice may use different
|
||||
* bit to set QE on different memories, so the MFR can't
|
||||
* indicate the quad_enable method for this case, we need
|
||||
* to set it in the default_init fixup hook.
|
||||
* GD25Q256C supports the first version of JESD216 which does not define
|
||||
* the Quad Enable methods. Overwrite the default Quad Enable method.
|
||||
*
|
||||
* GD25Q256 GENERATION | SFDP MAJOR VERSION | SFDP MINOR VERSION
|
||||
* GD25Q256C | SFDP_JESD216_MAJOR | SFDP_JESD216_MINOR
|
||||
* GD25Q256D | SFDP_JESD216_MAJOR | SFDP_JESD216B_MINOR
|
||||
* GD25Q256E | SFDP_JESD216_MAJOR | SFDP_JESD216B_MINOR
|
||||
*/
|
||||
nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable;
|
||||
if (bfpt_header->major == SFDP_JESD216_MAJOR &&
|
||||
bfpt_header->minor == SFDP_JESD216_MINOR)
|
||||
nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_nor_fixups gd25q256_fixups = {
|
||||
.default_init = gd25q256_default_init,
|
||||
.post_bfpt = gd25q256_post_bfpt,
|
||||
};
|
||||
|
||||
static const struct flash_info gigadevice_nor_parts[] = {
|
||||
|
@ -70,9 +70,10 @@ static const struct flash_info issi_nor_parts[] = {
|
||||
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||
{ "is25wp128", INFO(0x9d7018, 0, 64 * 1024, 256)
|
||||
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||
{ "is25wp256", INFO(0x9d7019, 0, 64 * 1024, 512)
|
||||
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
|
||||
{ "is25wp256", INFO(0x9d7019, 0, 0, 0)
|
||||
PARSE_SFDP
|
||||
FIXUP_FLAGS(SPI_NOR_4B_OPCODES)
|
||||
FLAGS(SPI_NOR_QUAD_PP)
|
||||
.fixups = &is25lp256_fixups },
|
||||
|
||||
/* PMC */
|
||||
|
@ -52,18 +52,21 @@ static int micron_st_nor_octal_dtr_en(struct spi_nor *nor)
|
||||
struct spi_mem_op op;
|
||||
u8 *buf = nor->bouncebuf;
|
||||
int ret;
|
||||
u8 addr_mode_nbytes = nor->params->addr_mode_nbytes;
|
||||
|
||||
/* Use 20 dummy cycles for memory array reads. */
|
||||
*buf = 20;
|
||||
op = (struct spi_mem_op)
|
||||
MICRON_ST_NOR_WR_ANY_REG_OP(3, SPINOR_REG_MT_CFR1V, 1, buf);
|
||||
MICRON_ST_NOR_WR_ANY_REG_OP(addr_mode_nbytes,
|
||||
SPINOR_REG_MT_CFR1V, 1, buf);
|
||||
ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
buf[0] = SPINOR_MT_OCT_DTR;
|
||||
op = (struct spi_mem_op)
|
||||
MICRON_ST_NOR_WR_ANY_REG_OP(3, SPINOR_REG_MT_CFR0V, 1, buf);
|
||||
MICRON_ST_NOR_WR_ANY_REG_OP(addr_mode_nbytes,
|
||||
SPINOR_REG_MT_CFR0V, 1, buf);
|
||||
ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -98,7 +101,8 @@ static int micron_st_nor_octal_dtr_dis(struct spi_nor *nor)
|
||||
buf[0] = SPINOR_MT_EXSPI;
|
||||
buf[1] = SPINOR_REG_MT_CFR1V_DEF;
|
||||
op = (struct spi_mem_op)
|
||||
MICRON_ST_NOR_WR_ANY_REG_OP(4, SPINOR_REG_MT_CFR0V, 2, buf);
|
||||
MICRON_ST_NOR_WR_ANY_REG_OP(nor->addr_nbytes,
|
||||
SPINOR_REG_MT_CFR0V, 2, buf);
|
||||
ret = spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8_DTR);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -201,6 +205,8 @@ static const struct flash_info st_nor_parts[] = {
|
||||
MFR_FLAGS(USE_FSR)
|
||||
},
|
||||
{ "mt25qu256a", INFO6(0x20bb19, 0x104400, 64 * 1024, 512)
|
||||
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP |
|
||||
SPI_NOR_BP3_SR_BIT6)
|
||||
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
|
||||
FIXUP_FLAGS(SPI_NOR_4B_OPCODES)
|
||||
MFR_FLAGS(USE_FSR)
|
||||
|
@ -135,8 +135,7 @@ struct sfdp_4bait {
|
||||
/**
|
||||
* spi_nor_read_raw() - raw read of serial flash memory. read_opcode,
|
||||
* addr_nbytes and read_dummy members of the struct spi_nor
|
||||
* should be previously
|
||||
* set.
|
||||
* should be previously set.
|
||||
* @nor: pointer to a 'struct spi_nor'
|
||||
* @addr: offset in the serial flash memory
|
||||
* @len: number of bytes to read
|
||||
@ -1183,10 +1182,17 @@ static int spi_nor_parse_profile1(struct spi_nor *nor,
|
||||
dummy = round_up(dummy, 2);
|
||||
|
||||
/* Update the fast read settings. */
|
||||
nor->params->hwcaps.mask |= SNOR_HWCAPS_READ_8_8_8_DTR;
|
||||
spi_nor_set_read_settings(&nor->params->reads[SNOR_CMD_READ_8_8_8_DTR],
|
||||
0, dummy, opcode,
|
||||
SNOR_PROTO_8_8_8_DTR);
|
||||
|
||||
/*
|
||||
* Page Program is "Required Command" in the xSPI Profile 1.0. Update
|
||||
* the params->hwcaps.mask here.
|
||||
*/
|
||||
nor->params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR;
|
||||
|
||||
out:
|
||||
kfree(dwords);
|
||||
return ret;
|
||||
@ -1249,6 +1255,33 @@ static void spi_nor_post_sfdp_fixups(struct spi_nor *nor)
|
||||
nor->info->fixups->post_sfdp(nor);
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_check_sfdp_signature() - check for a valid SFDP signature
|
||||
* @nor: pointer to a 'struct spi_nor'
|
||||
*
|
||||
* Used to detect if the flash supports the RDSFDP command as well as the
|
||||
* presence of a valid SFDP table.
|
||||
*
|
||||
* Return: 0 on success, -errno otherwise.
|
||||
*/
|
||||
int spi_nor_check_sfdp_signature(struct spi_nor *nor)
|
||||
{
|
||||
u32 signature;
|
||||
int err;
|
||||
|
||||
/* Get the SFDP header. */
|
||||
err = spi_nor_read_sfdp_dma_unsafe(nor, 0, sizeof(signature),
|
||||
&signature);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Check the SFDP signature. */
|
||||
if (le32_to_cpu(signature) != SFDP_SIGNATURE)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters.
|
||||
* @nor: pointer to a 'struct spi_nor'
|
||||
|
@ -107,6 +107,4 @@ struct sfdp_parameter_header {
|
||||
u8 id_msb;
|
||||
};
|
||||
|
||||
int spi_nor_parse_sfdp(struct spi_nor *nor);
|
||||
|
||||
#endif /* __LINUX_MTD_SFDP_H */
|
||||
|
@ -49,11 +49,13 @@ static int cypress_nor_octal_dtr_en(struct spi_nor *nor)
|
||||
struct spi_mem_op op;
|
||||
u8 *buf = nor->bouncebuf;
|
||||
int ret;
|
||||
u8 addr_mode_nbytes = nor->params->addr_mode_nbytes;
|
||||
|
||||
/* Use 24 dummy cycles for memory array reads. */
|
||||
*buf = SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24;
|
||||
op = (struct spi_mem_op)
|
||||
CYPRESS_NOR_WR_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR2V, 1, buf);
|
||||
CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes,
|
||||
SPINOR_REG_CYPRESS_CFR2V, 1, buf);
|
||||
|
||||
ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
|
||||
if (ret)
|
||||
@ -64,14 +66,16 @@ static int cypress_nor_octal_dtr_en(struct spi_nor *nor)
|
||||
/* Set the octal and DTR enable bits. */
|
||||
buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN;
|
||||
op = (struct spi_mem_op)
|
||||
CYPRESS_NOR_WR_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR5V, 1, buf);
|
||||
CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes,
|
||||
SPINOR_REG_CYPRESS_CFR5V, 1, buf);
|
||||
|
||||
ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Read flash ID to make sure the switch was successful. */
|
||||
ret = spi_nor_read_id(nor, 4, 3, buf, SNOR_PROTO_8_8_8_DTR);
|
||||
ret = spi_nor_read_id(nor, nor->addr_nbytes, 3, buf,
|
||||
SNOR_PROTO_8_8_8_DTR);
|
||||
if (ret) {
|
||||
dev_dbg(nor->dev, "error %d reading JEDEC ID after enabling 8D-8D-8D mode\n", ret);
|
||||
return ret;
|
||||
@ -97,7 +101,8 @@ static int cypress_nor_octal_dtr_dis(struct spi_nor *nor)
|
||||
buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS;
|
||||
buf[1] = 0;
|
||||
op = (struct spi_mem_op)
|
||||
CYPRESS_NOR_WR_ANY_REG_OP(4, SPINOR_REG_CYPRESS_CFR5V, 2, buf);
|
||||
CYPRESS_NOR_WR_ANY_REG_OP(nor->addr_nbytes,
|
||||
SPINOR_REG_CYPRESS_CFR5V, 2, buf);
|
||||
ret = spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8_DTR);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -191,7 +196,8 @@ static int cypress_nor_quad_enable_volatile(struct spi_nor *nor)
|
||||
static int cypress_nor_set_page_size(struct spi_nor *nor)
|
||||
{
|
||||
struct spi_mem_op op =
|
||||
CYPRESS_NOR_RD_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR3V,
|
||||
CYPRESS_NOR_RD_ANY_REG_OP(nor->params->addr_mode_nbytes,
|
||||
SPINOR_REG_CYPRESS_CFR3V,
|
||||
nor->bouncebuf);
|
||||
int ret;
|
||||
|
||||
@ -275,13 +281,7 @@ static int cypress_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
|
||||
cypress_nor_octal_dtr_dis(nor);
|
||||
}
|
||||
|
||||
static void s28hs512t_default_init(struct spi_nor *nor)
|
||||
{
|
||||
nor->params->octal_dtr_enable = cypress_nor_octal_dtr_enable;
|
||||
nor->params->writesize = 16;
|
||||
}
|
||||
|
||||
static void s28hs512t_post_sfdp_fixup(struct spi_nor *nor)
|
||||
static void s28hx_t_post_sfdp_fixup(struct spi_nor *nor)
|
||||
{
|
||||
/*
|
||||
* On older versions of the flash the xSPI Profile 1.0 table has the
|
||||
@ -309,17 +309,23 @@ static void s28hs512t_post_sfdp_fixup(struct spi_nor *nor)
|
||||
nor->params->rdsr_addr_nbytes = 4;
|
||||
}
|
||||
|
||||
static int s28hs512t_post_bfpt_fixup(struct spi_nor *nor,
|
||||
const struct sfdp_parameter_header *bfpt_header,
|
||||
const struct sfdp_bfpt *bfpt)
|
||||
static int s28hx_t_post_bfpt_fixup(struct spi_nor *nor,
|
||||
const struct sfdp_parameter_header *bfpt_header,
|
||||
const struct sfdp_bfpt *bfpt)
|
||||
{
|
||||
return cypress_nor_set_page_size(nor);
|
||||
}
|
||||
|
||||
static const struct spi_nor_fixups s28hs512t_fixups = {
|
||||
.default_init = s28hs512t_default_init,
|
||||
.post_sfdp = s28hs512t_post_sfdp_fixup,
|
||||
.post_bfpt = s28hs512t_post_bfpt_fixup,
|
||||
static void s28hx_t_late_init(struct spi_nor *nor)
|
||||
{
|
||||
nor->params->octal_dtr_enable = cypress_nor_octal_dtr_enable;
|
||||
nor->params->writesize = 16;
|
||||
}
|
||||
|
||||
static const struct spi_nor_fixups s28hx_t_fixups = {
|
||||
.post_sfdp = s28hx_t_post_sfdp_fixup,
|
||||
.post_bfpt = s28hx_t_post_bfpt_fixup,
|
||||
.late_init = s28hx_t_late_init,
|
||||
};
|
||||
|
||||
static int
|
||||
@ -453,10 +459,21 @@ static const struct flash_info spansion_nor_parts[] = {
|
||||
.fixups = &s25hx_t_fixups },
|
||||
{ "cy15x104q", INFO6(0x042cc2, 0x7f7f7f, 512 * 1024, 1)
|
||||
FLAGS(SPI_NOR_NO_ERASE) },
|
||||
{ "s28hl512t", INFO(0x345a1a, 0, 256 * 1024, 256)
|
||||
PARSE_SFDP
|
||||
.fixups = &s28hx_t_fixups,
|
||||
},
|
||||
{ "s28hl01gt", INFO(0x345a1b, 0, 256 * 1024, 512)
|
||||
PARSE_SFDP
|
||||
.fixups = &s28hx_t_fixups,
|
||||
},
|
||||
{ "s28hs512t", INFO(0x345b1a, 0, 256 * 1024, 256)
|
||||
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_OCTAL_DTR_READ |
|
||||
SPI_NOR_OCTAL_DTR_PP)
|
||||
.fixups = &s28hs512t_fixups,
|
||||
PARSE_SFDP
|
||||
.fixups = &s28hx_t_fixups,
|
||||
},
|
||||
{ "s28hs01gt", INFO(0x345b1b, 0, 256 * 1024, 512)
|
||||
PARSE_SFDP
|
||||
.fixups = &s28hx_t_fixups,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -35,8 +35,10 @@ static ssize_t jedec_id_show(struct device *dev,
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct spi_mem *spimem = spi_get_drvdata(spi);
|
||||
struct spi_nor *nor = spi_mem_get_drvdata(spimem);
|
||||
const u8 *id = nor->info->id_len ? nor->info->id : nor->id;
|
||||
u8 id_len = nor->info->id_len ?: SPI_NOR_MAX_ID_LEN;
|
||||
|
||||
return sysfs_emit(buf, "%*phN\n", nor->info->id_len, nor->info->id);
|
||||
return sysfs_emit(buf, "%*phN\n", id_len, id);
|
||||
}
|
||||
static DEVICE_ATTR_RO(jedec_id);
|
||||
|
||||
@ -67,6 +69,21 @@ static struct bin_attribute *spi_nor_sysfs_bin_entries[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static umode_t spi_nor_sysfs_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int n)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(kobj_to_dev(kobj));
|
||||
struct spi_mem *spimem = spi_get_drvdata(spi);
|
||||
struct spi_nor *nor = spi_mem_get_drvdata(spimem);
|
||||
|
||||
if (attr == &dev_attr_manufacturer.attr && !nor->manufacturer)
|
||||
return 0;
|
||||
if (attr == &dev_attr_jedec_id.attr && !nor->info->id_len && !nor->id)
|
||||
return 0;
|
||||
|
||||
return 0444;
|
||||
}
|
||||
|
||||
static umode_t spi_nor_sysfs_is_bin_visible(struct kobject *kobj,
|
||||
struct bin_attribute *attr, int n)
|
||||
{
|
||||
@ -82,6 +99,7 @@ static umode_t spi_nor_sysfs_is_bin_visible(struct kobject *kobj,
|
||||
|
||||
static const struct attribute_group spi_nor_sysfs_group = {
|
||||
.name = "spi-nor",
|
||||
.is_visible = spi_nor_sysfs_is_visible,
|
||||
.is_bin_visible = spi_nor_sysfs_is_bin_visible,
|
||||
.attrs = spi_nor_sysfs_entries,
|
||||
.bin_attrs = spi_nor_sysfs_bin_entries,
|
||||
|
@ -133,6 +133,9 @@ static const struct flash_info winbond_nor_parts[] = {
|
||||
{ "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024)
|
||||
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ |
|
||||
SPI_NOR_DUAL_READ) },
|
||||
{ "w25q512nwq", INFO(0xef6020, 0, 0, 0)
|
||||
PARSE_SFDP
|
||||
OTP_INFO(256, 3, 0x1000, 0x1000) },
|
||||
{ "w25q512nwm", INFO(0xef8020, 0, 64 * 1024, 1024)
|
||||
PARSE_SFDP
|
||||
OTP_INFO(256, 3, 0x1000, 0x1000) },
|
||||
|
@ -999,7 +999,6 @@ static inline bool nanddev_io_iter_end(struct nand_device *nand,
|
||||
|
||||
bool nanddev_isbad(struct nand_device *nand, const struct nand_pos *pos);
|
||||
bool nanddev_isreserved(struct nand_device *nand, const struct nand_pos *pos);
|
||||
int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos);
|
||||
int nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos);
|
||||
|
||||
/* ECC related functions */
|
||||
|
@ -349,6 +349,8 @@ struct spi_nor_flash_parameter;
|
||||
* @bouncebuf: bounce buffer used when the buffer passed by the MTD
|
||||
* layer is not DMA-able
|
||||
* @bouncebuf_size: size of the bounce buffer
|
||||
* @id: The flash's ID bytes. Always contains
|
||||
* SPI_NOR_MAX_ID_LEN bytes.
|
||||
* @info: SPI NOR part JEDEC MFR ID and other info
|
||||
* @manufacturer: SPI NOR manufacturer
|
||||
* @addr_nbytes: number of address bytes
|
||||
@ -379,6 +381,7 @@ struct spi_nor {
|
||||
struct spi_mem *spimem;
|
||||
u8 *bouncebuf;
|
||||
size_t bouncebuf_size;
|
||||
u8 *id;
|
||||
const struct flash_info *info;
|
||||
const struct spi_nor_manufacturer *manufacturer;
|
||||
u8 addr_nbytes;
|
||||
|
Loading…
Reference in New Issue
Block a user