forked from Minki/linux
Core MTD changes:
* mtdchar: add MEMREAD ioctl * Add ECC error accounting for each read request * always initialize 'stats' in struct mtd_oob_ops * Track maximum number of bitflips for each read request * Fix repeated word in comment * Move from strlcpy with unused retval to strscpy * Fix a typo in a comment * Add binding for U-Boot bootloader partitions MTD device drivers changes: * FTL: use container_of() rather than cast * docg3: - Use correct function names in comment blocks - Check the return value of devm_ioremap() in the probe * physmap-core: Fix NULL pointer dereferencing in of_select_probe_type() * parsers: add Broadcom's U-Boot parser Raw NAND core changes: * Replace of_gpio_named_count() by gpiod_count() - Remove misguided comment of nand_get_device() - bbt: Use the bitmap API to allocate bitmaps Raw NAND controller drivers changes: * Meson: - Stop supporting legacy clocks - Refine resource getting in probe - Convert bindings to yaml - Fix clock handling and update the bindings accordingly - Fix bit map use in meson_nfc_ecc_correct() * bcm47xx: - Fix spelling typo in comment * STM32 FMC2: - Switch to using devm_fwnode_gpiod_get() - Fix dma_map_sg error check * Cadence: - Remove an unneeded result variable * Marvell: - Fix error handle regarding dma_map_sg * Orion: - Use devm_clk_get_optional() * Cafe: - Use correct function name in comment block * Atmel: - Unmap streaming DMA mappings * Arasan: - Stop using 0 as NULL pointer * GPMI: - Fix typo 'the the' in comment * BRCM: - Add individual glue driver selection - Move Kconfig to driver folder * FSL: Fix none ECC mode * Intel: - Use devm_platform_ioremap_resource_byname() - Remove unused clk_rate member from struct ebu_nand - Remove unused nand_pa member from ebu_nand_cs - Don't re-define NAND_DATA_IFACE_CHECK_ONLY - Remove undocumented compatible string - Fix compatible string in the bindings - Read the chip-select line from the correct OF node - Fix maximum chip select value in the bindings -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEE9HuaYnbmDhq/XIDIJWrqGEe9VoQFAmNAPlUACgkQJWrqGEe9 VoTQogf/VPcMpDXORmgCm0HldghXsY2WqY92xUCNDEtREUmqLe+mEVuXlucWQdeM tthHNDhLIJQKDfmGdJiOBOqAZ5D6tjT+ZjTySOGP7TH2gzd5XmGGhFZlblioqryn oqE2XmPs72hKaFJhcC00Y/DFeaD/EsDolyLgwl9s6hI/JGPLcRFa8Z4gjRkWW8B9 NZEk5mB1AVm2MVjXF0OkT/Pm/FtFQ1HpprV4qhG2ZOHcL1GeDrGddNCZIf3vXgKY PXHCdjhej+zusHxVsYgkH0ju3n7OYTuxctLPbmK7Gqi3QeBj1SbpZWHEjyPrerFu jj8W8OXErxNQN49jngnbUa0ZKwuwag== =6/gy -----END PGP SIGNATURE----- Merge tag 'mtd/for-6.1' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux Pull MTD updates from Miquel Raynal: "Core MTD changes: - mtdchar: add MEMREAD ioctl - Add ECC error accounting for each read request - always initialize 'stats' in struct mtd_oob_ops - Track maximum number of bitflips for each read request - Fix repeated word in comment - Move from strlcpy with unused retval to strscpy - Fix a typo in a comment - Add binding for U-Boot bootloader partitions MTD device drivers changes: - FTL: use container_of() rather than cast - docg3: - Use correct function names in comment blocks - Check the return value of devm_ioremap() in the probe - physmap-core: Fix NULL pointer dereferencing in of_select_probe_type() - parsers: add Broadcom's U-Boot parser Raw NAND core changes: - Replace of_gpio_named_count() by gpiod_count() - Remove misguided comment of nand_get_device() - bbt: Use the bitmap API to allocate bitmaps Raw NAND controller drivers changes: - Meson: - Stop supporting legacy clocks - Refine resource getting in probe - Convert bindings to yaml - Fix clock handling and update the bindings accordingly - Fix bit map use in meson_nfc_ecc_correct() - bcm47xx: - Fix spelling typo in comment - STM32 FMC2: - Switch to using devm_fwnode_gpiod_get() - Fix dma_map_sg error check - Cadence: - Remove an unneeded result variable - Marvell: - Fix error handle regarding dma_map_sg - Orion: - Use devm_clk_get_optional() - Cafe: - Use correct function name in comment block - Atmel: - Unmap streaming DMA mappings - Arasan: - Stop using 0 as NULL pointer - GPMI: - Fix typo 'the the' in comment - BRCM: - Add individual glue driver selection - Move Kconfig to driver folder - FSL: Fix none ECC mode - Intel: - Use devm_platform_ioremap_resource_byname() - Remove unused clk_rate member from struct ebu_nand - Remove unused nand_pa member from ebu_nand_cs - Don't re-define NAND_DATA_IFACE_CHECK_ONLY - Remove undocumented compatible string - Fix compatible string in the bindings - Read the chip-select line from the correct OF node - Fix maximum chip select value in the bindings" * tag 'mtd/for-6.1' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux: (43 commits) mtd: rawnand: meson: stop supporting legacy clocks dt-bindings: nand: meson: convert txt to yaml mtd: rawnand: meson: refine resource getting in probe mtd: rawnand: meson: fix the clock dt-bindings: nand: meson: fix meson nfc clock mtd: rawnand: bcm47xx: fix spelling typo in comment mtd: rawnand: stm32_fmc2: switch to using devm_fwnode_gpiod_get() mtd: rawnand: cadence: Remove an unneeded result variable mtd: rawnand: Replace of_gpio_named_count() by gpiod_count() mtd: rawnand: marvell: Fix error handle regarding dma_map_sg mtd: rawnand: stm32_fmc2: Fix dma_map_sg error check mtd: rawnand: remove misguided comment of nand_get_device() mtd: rawnand: orion: Use devm_clk_get_optional() mtd: rawnand: cafe: Use correct function name in comment block mtd: rawnand: atmel: Unmap streaming DMA mappings mtd: rawnand: meson: fix bit map use in meson_nfc_ecc_correct() mtd: rawnand: arasan: stop using 0 as NULL pointer mtd: rawnand: gpmi: Fix typo 'the the' in comment mtd: rawnand: brcmnand: Add individual glue driver selection mtd: rawnand: brcmnand: Move Kconfig to driver folder ...
This commit is contained in:
commit
1227db9eab
@ -1,60 +0,0 @@
|
||||
Amlogic NAND Flash Controller (NFC) for GXBB/GXL/AXG family SoCs
|
||||
|
||||
This file documents the properties in addition to those available in
|
||||
the MTD NAND bindings.
|
||||
|
||||
Required properties:
|
||||
- compatible : contains one of:
|
||||
- "amlogic,meson-gxl-nfc"
|
||||
- "amlogic,meson-axg-nfc"
|
||||
- clocks :
|
||||
A list of phandle + clock-specifier pairs for the clocks listed
|
||||
in clock-names.
|
||||
|
||||
- clock-names: Should contain the following:
|
||||
"core" - NFC module gate clock
|
||||
"device" - device clock from eMMC sub clock controller
|
||||
"rx" - rx clock phase
|
||||
"tx" - tx clock phase
|
||||
|
||||
- amlogic,mmc-syscon : Required for NAND clocks, it's shared with SD/eMMC
|
||||
controller port C
|
||||
|
||||
Optional children nodes:
|
||||
Children nodes represent the available nand chips.
|
||||
|
||||
Other properties:
|
||||
see Documentation/devicetree/bindings/mtd/nand-controller.yaml for generic bindings.
|
||||
|
||||
Example demonstrate on AXG SoC:
|
||||
|
||||
sd_emmc_c_clkc: mmc@7000 {
|
||||
compatible = "amlogic,meson-axg-mmc-clkc", "syscon";
|
||||
reg = <0x0 0x7000 0x0 0x800>;
|
||||
};
|
||||
|
||||
nand-controller@7800 {
|
||||
compatible = "amlogic,meson-axg-nfc";
|
||||
reg = <0x0 0x7800 0x0 0x100>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
interrupts = <GIC_SPI 34 IRQ_TYPE_EDGE_RISING>;
|
||||
|
||||
clocks = <&clkc CLKID_SD_EMMC_C>,
|
||||
<&sd_emmc_c_clkc CLKID_MMC_DIV>,
|
||||
<&sd_emmc_c_clkc CLKID_MMC_PHASE_RX>,
|
||||
<&sd_emmc_c_clkc CLKID_MMC_PHASE_TX>;
|
||||
clock-names = "core", "device", "rx", "tx";
|
||||
amlogic,mmc-syscon = <&sd_emmc_c_clkc>;
|
||||
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&nand_pins>;
|
||||
|
||||
nand@0 {
|
||||
reg = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
nand-on-flash-bbt;
|
||||
};
|
||||
};
|
@ -0,0 +1,93 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mtd/amlogic,meson-nand.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Amlogic NAND Flash Controller (NFC) for GXBB/GXL/AXG family SoCs
|
||||
|
||||
allOf:
|
||||
- $ref: nand-controller.yaml
|
||||
|
||||
maintainers:
|
||||
- liang.yang@amlogic.com
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- amlogic,meson-gxl-nfc
|
||||
- amlogic,meson-axg-nfc
|
||||
|
||||
reg:
|
||||
maxItems: 2
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: nfc
|
||||
- const: emmc
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
minItems: 2
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: core
|
||||
- const: device
|
||||
|
||||
patternProperties:
|
||||
"^nand@[0-7]$":
|
||||
type: object
|
||||
properties:
|
||||
reg:
|
||||
minimum: 0
|
||||
maximum: 1
|
||||
|
||||
nand-ecc-mode:
|
||||
const: hw
|
||||
|
||||
nand-ecc-step-size:
|
||||
const: 1024
|
||||
|
||||
nand-ecc-strength:
|
||||
enum: [8, 16, 24, 30, 40, 50, 60]
|
||||
description: |
|
||||
The ECC configurations that can be supported are as follows.
|
||||
meson-gxl-nfc 8, 16, 24, 30, 40, 50, 60
|
||||
meson-axg-nfc 8
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/axg-clkc.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
nand-controller@ffe07800 {
|
||||
compatible = "amlogic,meson-axg-nfc";
|
||||
reg = <0xffe07800 0x100>, <0xffe07000 0x800>;
|
||||
reg-names = "nfc", "emmc";
|
||||
interrupts = <GIC_SPI 34 IRQ_TYPE_EDGE_RISING>;
|
||||
clocks = <&clkc CLKID_SD_EMMC_C>, <&clkc CLKID_FCLK_DIV2>;
|
||||
clock-names = "core", "device";
|
||||
|
||||
pinctrl-0 = <&nand_pins>;
|
||||
pinctrl-names = "default";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
nand@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
@ -1,7 +1,7 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mtd/intel,lgm-nand.yaml#
|
||||
$id: http://devicetree.org/schemas/mtd/intel,lgm-ebunand.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Intel LGM SoC NAND Controller Device Tree Bindings
|
||||
@ -14,7 +14,7 @@ maintainers:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: intel,lgm-nand
|
||||
const: intel,lgm-ebunand
|
||||
|
||||
reg:
|
||||
maxItems: 6
|
||||
@ -51,7 +51,7 @@ patternProperties:
|
||||
properties:
|
||||
reg:
|
||||
minimum: 0
|
||||
maximum: 7
|
||||
maximum: 1
|
||||
|
||||
nand-ecc-mode: true
|
||||
|
||||
@ -75,7 +75,7 @@ additionalProperties: false
|
||||
examples:
|
||||
- |
|
||||
nand-controller@e0f00000 {
|
||||
compatible = "intel,lgm-nand";
|
||||
compatible = "intel,lgm-ebunand";
|
||||
reg = <0xe0f00000 0x100>,
|
||||
<0xe1000000 0x300>,
|
||||
<0xe1400000 0x8000>,
|
49
Documentation/devicetree/bindings/mtd/partitions/u-boot.yaml
Normal file
49
Documentation/devicetree/bindings/mtd/partitions/u-boot.yaml
Normal file
@ -0,0 +1,49 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mtd/partitions/u-boot.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: U-Boot bootloader partition
|
||||
|
||||
description: |
|
||||
U-Boot is a bootlodaer commonly used in embedded devices. It's almost always
|
||||
located on some kind of flash device.
|
||||
|
||||
Device configuration is stored as a set of environment variables that are
|
||||
located in a (usually standalone) block of data.
|
||||
|
||||
maintainers:
|
||||
- Rafał Miłecki <rafal@milecki.pl>
|
||||
|
||||
allOf:
|
||||
- $ref: partition.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: brcm,u-boot
|
||||
description: |
|
||||
Broadcom stores environment variables inside a U-Boot partition. They
|
||||
can be identified by a custom header with magic value.
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
partition@0 {
|
||||
compatible = "brcm,u-boot";
|
||||
reg = <0x0 0x100000>;
|
||||
label = "u-boot";
|
||||
};
|
||||
|
||||
partition@100000 {
|
||||
reg = <0x100000 0x1ff00000>;
|
||||
label = "firmware";
|
||||
};
|
||||
};
|
@ -461,7 +461,7 @@ static int block2mtd_setup(const char *val, const struct kernel_param *kp)
|
||||
the device (even kmalloc() fails). Deter that work to
|
||||
block2mtd_setup2(). */
|
||||
|
||||
strlcpy(block2mtd_paramline, val, sizeof(block2mtd_paramline));
|
||||
strscpy(block2mtd_paramline, val, sizeof(block2mtd_paramline));
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
|
@ -300,7 +300,7 @@ static void doc_write_data_area(struct docg3 *docg3, const void *buf, int len)
|
||||
}
|
||||
|
||||
/**
|
||||
* doc_set_data_mode - Sets the flash to normal or reliable data mode
|
||||
* doc_set_reliable_mode - Sets the flash to normal or reliable data mode
|
||||
* @docg3: the device
|
||||
*
|
||||
* The reliable data mode is a bit slower than the fast mode, but less errors
|
||||
@ -442,7 +442,7 @@ static void doc_setup_writeaddr_sector(struct docg3 *docg3, int sector, int ofs)
|
||||
}
|
||||
|
||||
/**
|
||||
* doc_seek - Set both flash planes to the specified block, page for reading
|
||||
* doc_read_seek - Set both flash planes to the specified block, page for reading
|
||||
* @docg3: the device
|
||||
* @block0: the first plane block index
|
||||
* @block1: the second plane block index
|
||||
@ -871,6 +871,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
u8 *buf = ops->datbuf;
|
||||
size_t len, ooblen, nbdata, nboob;
|
||||
u8 hwecc[DOC_ECC_BCH_SIZE], eccconf1;
|
||||
struct mtd_ecc_stats old_stats;
|
||||
int max_bitflips = 0;
|
||||
|
||||
if (buf)
|
||||
@ -895,6 +896,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
ret = 0;
|
||||
skip = from % DOC_LAYOUT_PAGE_SIZE;
|
||||
mutex_lock(&docg3->cascade->lock);
|
||||
old_stats = mtd->ecc_stats;
|
||||
while (ret >= 0 && (len > 0 || ooblen > 0)) {
|
||||
calc_block_sector(from - skip, &block0, &block1, &page, &ofs,
|
||||
docg3->reliable);
|
||||
@ -966,6 +968,12 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
}
|
||||
|
||||
out:
|
||||
if (ops->stats) {
|
||||
ops->stats->uncorrectable_errors +=
|
||||
mtd->ecc_stats.failed - old_stats.failed;
|
||||
ops->stats->corrected_bitflips +=
|
||||
mtd->ecc_stats.corrected - old_stats.corrected;
|
||||
}
|
||||
mutex_unlock(&docg3->cascade->lock);
|
||||
return ret;
|
||||
err_in_read:
|
||||
@ -1951,7 +1959,7 @@ static int docg3_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
}
|
||||
|
||||
/**
|
||||
* doc_probe - Probe the IO space for a DiskOnChip G3 chip
|
||||
* docg3_probe - Probe the IO space for a DiskOnChip G3 chip
|
||||
* @pdev: platform device
|
||||
*
|
||||
* Probes for a G3 chip at the specified IO space in the platform data
|
||||
@ -1974,9 +1982,14 @@ static int __init docg3_probe(struct platform_device *pdev)
|
||||
dev_err(dev, "No I/O memory resource defined\n");
|
||||
return ret;
|
||||
}
|
||||
base = devm_ioremap(dev, ress->start, DOC_IOSPACE_SIZE);
|
||||
|
||||
ret = -ENOMEM;
|
||||
base = devm_ioremap(dev, ress->start, DOC_IOSPACE_SIZE);
|
||||
if (!base) {
|
||||
dev_err(dev, "devm_ioremap dev failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
cascade = devm_kcalloc(dev, DOC_MAX_NBFLOORS, sizeof(*cascade),
|
||||
GFP_KERNEL);
|
||||
if (!cascade)
|
||||
|
@ -941,7 +941,7 @@ static int ftl_write(partition_t *part, caddr_t buffer,
|
||||
|
||||
static int ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
|
||||
{
|
||||
partition_t *part = (void *)dev;
|
||||
partition_t *part = container_of(dev, struct partition_t, mbd);
|
||||
u_long sect;
|
||||
|
||||
/* Sort of arbitrary: round size down to 4KiB boundary */
|
||||
@ -969,7 +969,7 @@ static int ftl_writesect(struct mtd_blktrans_dev *dev,
|
||||
static int ftl_discardsect(struct mtd_blktrans_dev *dev,
|
||||
unsigned long sector, unsigned nr_sects)
|
||||
{
|
||||
partition_t *part = (void *)dev;
|
||||
partition_t *part = container_of(dev, struct partition_t, mbd);
|
||||
uint32_t bsize = 1 << part->header.EraseUnitSize;
|
||||
|
||||
pr_debug("FTL erase sector %ld for %d sectors\n",
|
||||
|
@ -136,7 +136,7 @@ static void inftl_remove_dev(struct mtd_blktrans_dev *dev)
|
||||
int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
|
||||
size_t *retlen, uint8_t *buf)
|
||||
{
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
int res;
|
||||
|
||||
ops.mode = MTD_OPS_PLACE_OOB;
|
||||
@ -156,7 +156,7 @@ int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
|
||||
int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
|
||||
size_t *retlen, uint8_t *buf)
|
||||
{
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
int res;
|
||||
|
||||
ops.mode = MTD_OPS_PLACE_OOB;
|
||||
@ -176,7 +176,7 @@ int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
|
||||
static int inftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
|
||||
size_t *retlen, uint8_t *buf, uint8_t *oob)
|
||||
{
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
int res;
|
||||
|
||||
ops.mode = MTD_OPS_PLACE_OOB;
|
||||
|
@ -300,6 +300,9 @@ static const char *of_select_probe_type(struct platform_device *dev)
|
||||
const char *probe_type;
|
||||
|
||||
match = of_match_device(of_flash_match, &dev->dev);
|
||||
if (!match)
|
||||
return NULL;
|
||||
|
||||
probe_type = match->data;
|
||||
if (probe_type)
|
||||
return probe_type;
|
||||
|
@ -688,6 +688,137 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtdchar_read_ioctl(struct mtd_info *mtd,
|
||||
struct mtd_read_req __user *argp)
|
||||
{
|
||||
struct mtd_info *master = mtd_get_master(mtd);
|
||||
struct mtd_read_req req;
|
||||
void __user *usr_data, *usr_oob;
|
||||
uint8_t *datbuf = NULL, *oobbuf = NULL;
|
||||
size_t datbuf_len, oobbuf_len;
|
||||
size_t orig_len, orig_ooblen;
|
||||
int ret = 0;
|
||||
|
||||
if (copy_from_user(&req, argp, sizeof(req)))
|
||||
return -EFAULT;
|
||||
|
||||
orig_len = req.len;
|
||||
orig_ooblen = req.ooblen;
|
||||
|
||||
usr_data = (void __user *)(uintptr_t)req.usr_data;
|
||||
usr_oob = (void __user *)(uintptr_t)req.usr_oob;
|
||||
|
||||
if (!master->_read_oob)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!usr_data)
|
||||
req.len = 0;
|
||||
|
||||
if (!usr_oob)
|
||||
req.ooblen = 0;
|
||||
|
||||
req.ecc_stats.uncorrectable_errors = 0;
|
||||
req.ecc_stats.corrected_bitflips = 0;
|
||||
req.ecc_stats.max_bitflips = 0;
|
||||
|
||||
req.len &= 0xffffffff;
|
||||
req.ooblen &= 0xffffffff;
|
||||
|
||||
if (req.start + req.len > mtd->size) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
datbuf_len = min_t(size_t, req.len, mtd->erasesize);
|
||||
if (datbuf_len > 0) {
|
||||
datbuf = kvmalloc(datbuf_len, GFP_KERNEL);
|
||||
if (!datbuf) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
oobbuf_len = min_t(size_t, req.ooblen, mtd->erasesize);
|
||||
if (oobbuf_len > 0) {
|
||||
oobbuf = kvmalloc(oobbuf_len, GFP_KERNEL);
|
||||
if (!oobbuf) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
while (req.len > 0 || (!usr_data && req.ooblen > 0)) {
|
||||
struct mtd_req_stats stats;
|
||||
struct mtd_oob_ops ops = {
|
||||
.mode = req.mode,
|
||||
.len = min_t(size_t, req.len, datbuf_len),
|
||||
.ooblen = min_t(size_t, req.ooblen, oobbuf_len),
|
||||
.datbuf = datbuf,
|
||||
.oobbuf = oobbuf,
|
||||
.stats = &stats,
|
||||
};
|
||||
|
||||
/*
|
||||
* Shorten non-page-aligned, eraseblock-sized reads so that the
|
||||
* read ends on an eraseblock boundary. This is necessary in
|
||||
* order to prevent OOB data for some pages from being
|
||||
* duplicated in the output of non-page-aligned reads requiring
|
||||
* multiple mtd_read_oob() calls to be completed.
|
||||
*/
|
||||
if (ops.len == mtd->erasesize)
|
||||
ops.len -= mtd_mod_by_ws(req.start + ops.len, mtd);
|
||||
|
||||
ret = mtd_read_oob(mtd, (loff_t)req.start, &ops);
|
||||
|
||||
req.ecc_stats.uncorrectable_errors +=
|
||||
stats.uncorrectable_errors;
|
||||
req.ecc_stats.corrected_bitflips += stats.corrected_bitflips;
|
||||
req.ecc_stats.max_bitflips =
|
||||
max(req.ecc_stats.max_bitflips, stats.max_bitflips);
|
||||
|
||||
if (ret && !mtd_is_bitflip_or_eccerr(ret))
|
||||
break;
|
||||
|
||||
if (copy_to_user(usr_data, ops.datbuf, ops.retlen) ||
|
||||
copy_to_user(usr_oob, ops.oobbuf, ops.oobretlen)) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
req.start += ops.retlen;
|
||||
req.len -= ops.retlen;
|
||||
usr_data += ops.retlen;
|
||||
|
||||
req.ooblen -= ops.oobretlen;
|
||||
usr_oob += ops.oobretlen;
|
||||
}
|
||||
|
||||
/*
|
||||
* As multiple iterations of the above loop (and therefore multiple
|
||||
* mtd_read_oob() calls) may be necessary to complete the read request,
|
||||
* adjust the final return code to ensure it accounts for all detected
|
||||
* ECC errors.
|
||||
*/
|
||||
if (!ret || mtd_is_bitflip(ret)) {
|
||||
if (req.ecc_stats.uncorrectable_errors > 0)
|
||||
ret = -EBADMSG;
|
||||
else if (req.ecc_stats.corrected_bitflips > 0)
|
||||
ret = -EUCLEAN;
|
||||
}
|
||||
|
||||
out:
|
||||
req.len = orig_len - req.len;
|
||||
req.ooblen = orig_ooblen - req.ooblen;
|
||||
|
||||
if (copy_to_user(argp, &req, sizeof(req)))
|
||||
ret = -EFAULT;
|
||||
|
||||
kvfree(datbuf);
|
||||
kvfree(oobbuf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
|
||||
{
|
||||
struct mtd_file_info *mfi = file->private_data;
|
||||
@ -710,6 +841,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
|
||||
case MEMGETINFO:
|
||||
case MEMREADOOB:
|
||||
case MEMREADOOB64:
|
||||
case MEMREAD:
|
||||
case MEMISLOCKED:
|
||||
case MEMGETOOBSEL:
|
||||
case MEMGETBADBLOCK:
|
||||
@ -884,6 +1016,13 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
|
||||
break;
|
||||
}
|
||||
|
||||
case MEMREAD:
|
||||
{
|
||||
ret = mtdchar_read_ioctl(mtd,
|
||||
(struct mtd_read_req __user *)arg);
|
||||
break;
|
||||
}
|
||||
|
||||
case MEMLOCK:
|
||||
{
|
||||
struct erase_info_user einfo;
|
||||
|
@ -836,7 +836,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
|
||||
|
||||
/*
|
||||
* walk the map of the new device once more and fill in
|
||||
* in erase region info:
|
||||
* erase region info:
|
||||
*/
|
||||
curr_erasesize = subdev[0]->erasesize;
|
||||
begin = position = 0;
|
||||
|
@ -1624,6 +1624,9 @@ int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
|
||||
if (!master->_read_oob && (!master->_read || ops->oobbuf))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (ops->stats)
|
||||
memset(ops->stats, 0, sizeof(*ops->stats));
|
||||
|
||||
if (mtd->flags & MTD_SLC_ON_MLC_EMULATION)
|
||||
ret_code = mtd_io_emulated_slc(mtd, from, true, ops);
|
||||
else
|
||||
@ -1641,6 +1644,8 @@ int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
|
||||
return ret_code;
|
||||
if (mtd->ecc_strength == 0)
|
||||
return 0; /* device lacks ecc */
|
||||
if (ops->stats)
|
||||
ops->stats->max_bitflips = ret_code;
|
||||
return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_read_oob);
|
||||
|
@ -401,7 +401,7 @@ static void mtdpstore_notify_add(struct mtd_info *mtd)
|
||||
/*
|
||||
* kmsg_size must be aligned to 4096 Bytes, which is limited by
|
||||
* psblk. The default value of kmsg_size is 64KB. If kmsg_size
|
||||
* is larger than erasesize, some errors will occur since mtdpsotre
|
||||
* is larger than erasesize, some errors will occur since mtdpstore
|
||||
* is designed on it.
|
||||
*/
|
||||
if (mtd->erasesize < info->kmsg_size) {
|
||||
|
@ -323,7 +323,7 @@ static int mtdswap_read_markers(struct mtdswap_dev *d, struct swap_eb *eb)
|
||||
struct mtdswap_oobdata *data, *data2;
|
||||
int ret;
|
||||
loff_t offset;
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
|
||||
offset = mtdswap_eb_offset(d, eb);
|
||||
|
||||
@ -370,7 +370,7 @@ static int mtdswap_write_marker(struct mtdswap_dev *d, struct swap_eb *eb,
|
||||
struct mtdswap_oobdata n;
|
||||
int ret;
|
||||
loff_t offset;
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
|
||||
ops.ooboffs = 0;
|
||||
ops.oobbuf = (uint8_t *)&n;
|
||||
@ -878,7 +878,7 @@ static unsigned int mtdswap_eblk_passes(struct mtdswap_dev *d,
|
||||
loff_t base, pos;
|
||||
unsigned int *p1 = (unsigned int *)d->page_buf;
|
||||
unsigned char *p2 = (unsigned char *)d->oob_buf;
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
int ret;
|
||||
|
||||
ops.mode = MTD_OPS_AUTO_OOB;
|
||||
|
@ -24,11 +24,8 @@ int nanddev_bbt_init(struct nand_device *nand)
|
||||
{
|
||||
unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
|
||||
unsigned int nblocks = nanddev_neraseblocks(nand);
|
||||
unsigned int nwords = DIV_ROUND_UP(nblocks * bits_per_block,
|
||||
BITS_PER_LONG);
|
||||
|
||||
nand->bbt.cache = kcalloc(nwords, sizeof(*nand->bbt.cache),
|
||||
GFP_KERNEL);
|
||||
nand->bbt.cache = bitmap_zalloc(nblocks * bits_per_block, GFP_KERNEL);
|
||||
if (!nand->bbt.cache)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -44,7 +41,7 @@ EXPORT_SYMBOL_GPL(nanddev_bbt_init);
|
||||
*/
|
||||
void nanddev_bbt_cleanup(struct nand_device *nand)
|
||||
{
|
||||
kfree(nand->bbt.cache);
|
||||
bitmap_free(nand->bbt.cache);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nanddev_bbt_cleanup);
|
||||
|
||||
|
@ -1440,6 +1440,7 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
struct onenand_chip *this = mtd->priv;
|
||||
struct mtd_ecc_stats old_stats;
|
||||
int ret;
|
||||
|
||||
switch (ops->mode) {
|
||||
@ -1453,12 +1454,23 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
}
|
||||
|
||||
onenand_get_device(mtd, FL_READING);
|
||||
|
||||
old_stats = mtd->ecc_stats;
|
||||
|
||||
if (ops->datbuf)
|
||||
ret = ONENAND_IS_4KB_PAGE(this) ?
|
||||
onenand_mlc_read_ops_nolock(mtd, from, ops) :
|
||||
onenand_read_ops_nolock(mtd, from, ops);
|
||||
else
|
||||
ret = onenand_read_oob_nolock(mtd, from, ops);
|
||||
|
||||
if (ops->stats) {
|
||||
ops->stats->uncorrectable_errors +=
|
||||
mtd->ecc_stats.failed - old_stats.failed;
|
||||
ops->stats->corrected_bitflips +=
|
||||
mtd->ecc_stats.corrected - old_stats.corrected;
|
||||
}
|
||||
|
||||
onenand_release_device(mtd);
|
||||
|
||||
return ret;
|
||||
@ -2935,7 +2947,7 @@ static int do_otp_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
struct onenand_chip *this = mtd->priv;
|
||||
unsigned char *pbuf = buf;
|
||||
int ret;
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
|
||||
/* Force buffer page aligned */
|
||||
if (len < mtd->writesize) {
|
||||
@ -2977,7 +2989,7 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf)
|
||||
{
|
||||
struct onenand_chip *this = mtd->priv;
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
int ret;
|
||||
|
||||
if (FLEXONENAND(this)) {
|
||||
|
@ -61,7 +61,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
|
||||
int startblock;
|
||||
loff_t from;
|
||||
size_t readlen;
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
int rgn;
|
||||
|
||||
printk(KERN_INFO "Scanning device for bad blocks\n");
|
||||
|
@ -200,27 +200,7 @@ config MTD_NAND_TMIO
|
||||
Support for NAND flash connected to a Toshiba Mobile IO
|
||||
Controller in some PDAs, including the Sharp SL6000x.
|
||||
|
||||
config MTD_NAND_BRCMNAND
|
||||
tristate "Broadcom STB NAND controller"
|
||||
depends on ARM || ARM64 || MIPS || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables the Broadcom NAND controller driver. The controller was
|
||||
originally designed for Set-Top Box but is used on various BCM7xxx,
|
||||
BCM3xxx, BCM63xxx, iProc/Cygnus and more.
|
||||
|
||||
if MTD_NAND_BRCMNAND
|
||||
|
||||
config MTD_NAND_BRCMNAND_BCMA
|
||||
tristate "Broadcom BCMA NAND controller"
|
||||
depends on BCMA_NFLASH
|
||||
depends on BCMA
|
||||
help
|
||||
Enables the BRCMNAND controller over BCMA on BCM47186/BCM5358 SoCs.
|
||||
The glue driver will take care of performing the low-level I/O
|
||||
operations to interface the BRCMNAND controller over the BCMA bus.
|
||||
|
||||
endif # MTD_NAND_BRCMNAND
|
||||
source "drivers/mtd/nand/raw/brcmnand/Kconfig"
|
||||
|
||||
config MTD_NAND_BCM47XXNFLASH
|
||||
tristate "BCM4706 BCMA NAND controller"
|
||||
@ -410,7 +390,7 @@ config MTD_NAND_STM32_FMC2
|
||||
|
||||
config MTD_NAND_MESON
|
||||
tristate "Support for NAND controller on Amlogic's Meson SoCs"
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
depends on COMMON_CLK && (ARCH_MESON || COMPILE_TEST)
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Enables support for NAND controller on Amlogic's Meson SoCs.
|
||||
|
@ -915,7 +915,7 @@ static int anfc_check_op(struct nand_chip *chip,
|
||||
if (instr->ctx.data.len > ANFC_MAX_CHUNK_SIZE)
|
||||
return -ENOTSUPP;
|
||||
|
||||
if (anfc_pkt_len_config(instr->ctx.data.len, 0, 0))
|
||||
if (anfc_pkt_len_config(instr->ctx.data.len, NULL, NULL))
|
||||
return -ENOTSUPP;
|
||||
|
||||
break;
|
||||
|
@ -405,6 +405,7 @@ static int atmel_nand_dma_transfer(struct atmel_nand_controller *nc,
|
||||
|
||||
dma_async_issue_pending(nc->dmac);
|
||||
wait_for_completion(&finished);
|
||||
dma_unmap_single(nc->dev, buf_dma, len, dir);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include <linux/bcma/bcma.h>
|
||||
|
||||
/* Broadcom uses 1'000'000 but it seems to be too many. Tests on WNDR4500 has
|
||||
* shown ~1000 retries as maxiumum. */
|
||||
* shown ~1000 retries as maximum. */
|
||||
#define NFLASH_READY_RETRIES 10000
|
||||
|
||||
#define NFLASH_SECTOR_SIZE 512
|
||||
|
49
drivers/mtd/nand/raw/brcmnand/Kconfig
Normal file
49
drivers/mtd/nand/raw/brcmnand/Kconfig
Normal file
@ -0,0 +1,49 @@
|
||||
config MTD_NAND_BRCMNAND
|
||||
tristate "Broadcom STB NAND controller"
|
||||
depends on ARM || ARM64 || MIPS || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables the Broadcom NAND controller driver. The controller was
|
||||
originally designed for Set-Top Box but is used on various BCM7xxx,
|
||||
BCM3xxx, BCM63xxx, iProc/Cygnus and more.
|
||||
|
||||
if MTD_NAND_BRCMNAND
|
||||
|
||||
config MTD_NAND_BRCMNAND_BCM63XX
|
||||
tristate "Broadcom BCM63xx NAND controller glue"
|
||||
default BCM63XX
|
||||
help
|
||||
Enables the BRCMNAND glue driver to register the NAND controller
|
||||
on Broadcom BCM63xx MIPS-based DSL platforms.
|
||||
|
||||
config MTD_NAND_BRCMNAND_BCMA
|
||||
tristate "Broadcom BCMA NAND controller"
|
||||
depends on BCMA_NFLASH
|
||||
depends on BCMA
|
||||
help
|
||||
Enables the BRCMNAND controller over BCMA on BCM47186/BCM5358 SoCs.
|
||||
The glue driver will take care of performing the low-level I/O
|
||||
operations to interface the BRCMNAND controller over the BCMA bus.
|
||||
|
||||
config MTD_NAND_BRCMNAND_BCMBCA
|
||||
tristate "Broadcom BCMBCA NAND controller glue"
|
||||
default ARCH_BCMBCA
|
||||
help
|
||||
Enables the BRCMNAND glue driver to register the NAND controller
|
||||
on Broadcom BCA platforms.
|
||||
|
||||
config MTD_NAND_BRCMNAND_BRCMSTB
|
||||
tristate "Broadcom STB Nand controller glue"
|
||||
default ARCH_BRCMSTB
|
||||
help
|
||||
Enables the BRCMNAND glue driver to register the NAND controller
|
||||
on Broadcom STB platforms.
|
||||
|
||||
config MTD_NAND_BRCMNAND_IPROC
|
||||
tristate "Broadcom iProc NAND controller glue"
|
||||
default ARCH_BCM_IPROC
|
||||
help
|
||||
Enables the BRCMNAND controller glue driver to register the NAND
|
||||
controller on Broadcom iProc platforms.
|
||||
|
||||
endif # MTD_NAND_BRCMNAND
|
@ -1,10 +1,10 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# link order matters; don't link the more generic brcmstb_nand.o before the
|
||||
# more specific iproc_nand.o, for instance
|
||||
obj-$(CONFIG_MTD_NAND_BRCMNAND) += iproc_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_BRCMNAND) += bcm63138_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_BRCMNAND) += bcm6368_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmstb_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_BRCMNAND_IPROC) += iproc_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_BRCMNAND_BCMBCA) += bcm63138_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_BRCMNAND_BCM63XX) += bcm6368_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_BRCMNAND_BRCMSTB) += brcmstb_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand.o
|
||||
|
||||
obj-$(CONFIG_MTD_NAND_BRCMNAND_BCMA) += bcma_nand.o
|
||||
|
@ -1979,7 +1979,6 @@ static int cadence_nand_force_byte_access(struct nand_chip *chip,
|
||||
bool force_8bit)
|
||||
{
|
||||
struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller);
|
||||
int status;
|
||||
|
||||
/*
|
||||
* Callers of this function do not verify if the NAND is using a 16-bit
|
||||
@ -1990,9 +1989,7 @@ static int cadence_nand_force_byte_access(struct nand_chip *chip,
|
||||
if (!(chip->options & NAND_BUSWIDTH_16))
|
||||
return 0;
|
||||
|
||||
status = cadence_nand_set_access_width16(cdns_ctrl, !force_8bit);
|
||||
|
||||
return status;
|
||||
return cadence_nand_set_access_width16(cdns_ctrl, !force_8bit);
|
||||
}
|
||||
|
||||
static int cadence_nand_cmd_opcode(struct nand_chip *chip,
|
||||
|
@ -358,7 +358,7 @@ static int cafe_nand_read_oob(struct nand_chip *chip, int page)
|
||||
return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize);
|
||||
}
|
||||
/**
|
||||
* cafe_nand_read_page_syndrome - [REPLACEABLE] hardware ecc syndrome based page read
|
||||
* cafe_nand_read_page - [REPLACEABLE] hardware ecc syndrome based page read
|
||||
* @chip: nand chip info structure
|
||||
* @buf: buffer to store read data
|
||||
* @oob_required: caller expects OOB data read to chip->oob_poi
|
||||
|
@ -726,36 +726,40 @@ static int fsl_elbc_attach_chip(struct nand_chip *chip)
|
||||
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
|
||||
unsigned int al;
|
||||
|
||||
switch (chip->ecc.engine_type) {
|
||||
/*
|
||||
* if ECC was not chosen in DT, decide whether to use HW or SW ECC from
|
||||
* CS Base Register
|
||||
*/
|
||||
case NAND_ECC_ENGINE_TYPE_NONE:
|
||||
if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_INVALID) {
|
||||
/* If CS Base Register selects full hardware ECC then use it */
|
||||
if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) ==
|
||||
BR_DECC_CHK_GEN) {
|
||||
chip->ecc.read_page = fsl_elbc_read_page;
|
||||
chip->ecc.write_page = fsl_elbc_write_page;
|
||||
chip->ecc.write_subpage = fsl_elbc_write_subpage;
|
||||
|
||||
chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
|
||||
mtd_set_ooblayout(mtd, &fsl_elbc_ooblayout_ops);
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.bytes = 3;
|
||||
chip->ecc.strength = 1;
|
||||
} else {
|
||||
/* otherwise fall back to default software ECC */
|
||||
chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
|
||||
chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
|
||||
}
|
||||
}
|
||||
|
||||
switch (chip->ecc.engine_type) {
|
||||
/* if HW ECC was chosen, setup ecc and oob layout */
|
||||
case NAND_ECC_ENGINE_TYPE_ON_HOST:
|
||||
chip->ecc.read_page = fsl_elbc_read_page;
|
||||
chip->ecc.write_page = fsl_elbc_write_page;
|
||||
chip->ecc.write_subpage = fsl_elbc_write_subpage;
|
||||
mtd_set_ooblayout(mtd, &fsl_elbc_ooblayout_ops);
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.bytes = 3;
|
||||
chip->ecc.strength = 1;
|
||||
break;
|
||||
|
||||
/* if SW ECC was chosen in DT, we do not need to set anything here */
|
||||
/* if none or SW ECC was chosen, we do not need to set anything here */
|
||||
case NAND_ECC_ENGINE_TYPE_NONE:
|
||||
case NAND_ECC_ENGINE_TYPE_SOFT:
|
||||
case NAND_ECC_ENGINE_TYPE_ON_DIE:
|
||||
break;
|
||||
|
||||
/* should we also implement *_ECC_ENGINE_CONTROLLER to do as above? */
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -1361,7 +1361,7 @@ error_alloc:
|
||||
/*
|
||||
* Handles block mark swapping.
|
||||
* It can be called in swapping the block mark, or swapping it back,
|
||||
* because the the operations are the same.
|
||||
* because the operations are the same.
|
||||
*/
|
||||
static void block_mark_swapping(struct gpmi_nand_data *this,
|
||||
void *payload, void *auxiliary)
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
@ -99,15 +100,12 @@
|
||||
|
||||
#define HSNAND_ECC_OFFSET 0x008
|
||||
|
||||
#define NAND_DATA_IFACE_CHECK_ONLY -1
|
||||
|
||||
#define MAX_CS 2
|
||||
|
||||
#define USEC_PER_SEC 1000000L
|
||||
|
||||
struct ebu_nand_cs {
|
||||
void __iomem *chipaddr;
|
||||
dma_addr_t nand_pa;
|
||||
u32 addr_sel;
|
||||
};
|
||||
|
||||
@ -120,7 +118,6 @@ struct ebu_nand_controller {
|
||||
struct dma_chan *dma_tx;
|
||||
struct dma_chan *dma_rx;
|
||||
struct completion dma_access_complete;
|
||||
unsigned long clk_rate;
|
||||
struct clk *clk;
|
||||
u32 nd_para0;
|
||||
u8 cs_num;
|
||||
@ -580,6 +577,7 @@ static int ebu_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ebu_nand_controller *ebu_host;
|
||||
struct device_node *chip_np;
|
||||
struct nand_chip *nand;
|
||||
struct mtd_info *mtd;
|
||||
struct resource *res;
|
||||
@ -594,17 +592,20 @@ static int ebu_nand_probe(struct platform_device *pdev)
|
||||
ebu_host->dev = dev;
|
||||
nand_controller_init(&ebu_host->controller);
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ebunand");
|
||||
ebu_host->ebu = devm_ioremap_resource(&pdev->dev, res);
|
||||
ebu_host->ebu = devm_platform_ioremap_resource_byname(pdev, "ebunand");
|
||||
if (IS_ERR(ebu_host->ebu))
|
||||
return PTR_ERR(ebu_host->ebu);
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hsnand");
|
||||
ebu_host->hsnand = devm_ioremap_resource(&pdev->dev, res);
|
||||
ebu_host->hsnand = devm_platform_ioremap_resource_byname(pdev, "hsnand");
|
||||
if (IS_ERR(ebu_host->hsnand))
|
||||
return PTR_ERR(ebu_host->hsnand);
|
||||
|
||||
ret = device_property_read_u32(dev, "reg", &cs);
|
||||
chip_np = of_get_next_child(dev->of_node, NULL);
|
||||
if (!chip_np)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Could not find child node for the NAND chip\n");
|
||||
|
||||
ret = of_property_read_u32(chip_np, "reg", &cs);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get chip select: %d\n", ret);
|
||||
return ret;
|
||||
@ -617,11 +618,10 @@ static int ebu_nand_probe(struct platform_device *pdev)
|
||||
ebu_host->cs_num = cs;
|
||||
|
||||
resname = devm_kasprintf(dev, GFP_KERNEL, "nand_cs%d", cs);
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, resname);
|
||||
ebu_host->cs[cs].chipaddr = devm_ioremap_resource(dev, res);
|
||||
ebu_host->cs[cs].chipaddr = devm_platform_ioremap_resource_byname(pdev,
|
||||
resname);
|
||||
if (IS_ERR(ebu_host->cs[cs].chipaddr))
|
||||
return PTR_ERR(ebu_host->cs[cs].chipaddr);
|
||||
ebu_host->cs[cs].nand_pa = res->start;
|
||||
|
||||
ebu_host->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(ebu_host->clk))
|
||||
@ -633,7 +633,6 @@ static int ebu_nand_probe(struct platform_device *pdev)
|
||||
dev_err(dev, "failed to enable clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ebu_host->clk_rate = clk_get_rate(ebu_host->clk);
|
||||
|
||||
ebu_host->dma_tx = dma_request_chan(dev, "tx");
|
||||
if (IS_ERR(ebu_host->dma_tx)) {
|
||||
@ -660,7 +659,7 @@ static int ebu_nand_probe(struct platform_device *pdev)
|
||||
writel(ebu_host->cs[cs].addr_sel | EBU_ADDR_MASK(5) | EBU_ADDR_SEL_REGEN,
|
||||
ebu_host->ebu + EBU_ADDR_SEL(cs));
|
||||
|
||||
nand_set_flash_node(&ebu_host->chip, dev->of_node);
|
||||
nand_set_flash_node(&ebu_host->chip, chip_np);
|
||||
|
||||
mtd = nand_to_mtd(&ebu_host->chip);
|
||||
if (!mtd->name) {
|
||||
@ -716,7 +715,6 @@ static int ebu_nand_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static const struct of_device_id ebu_nand_match[] = {
|
||||
{ .compatible = "intel,nand-controller" },
|
||||
{ .compatible = "intel,lgm-ebunand" },
|
||||
{}
|
||||
};
|
||||
|
@ -865,13 +865,19 @@ static int marvell_nfc_xfer_data_dma(struct marvell_nfc *nfc,
|
||||
marvell_nfc_enable_dma(nfc);
|
||||
/* Prepare the DMA transfer */
|
||||
sg_init_one(&sg, nfc->dma_buf, dma_len);
|
||||
dma_map_sg(nfc->dma_chan->device->dev, &sg, 1, direction);
|
||||
ret = dma_map_sg(nfc->dma_chan->device->dev, &sg, 1, direction);
|
||||
if (!ret) {
|
||||
dev_err(nfc->dev, "Could not map DMA S/G list\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
tx = dmaengine_prep_slave_sg(nfc->dma_chan, &sg, 1,
|
||||
direction == DMA_FROM_DEVICE ?
|
||||
DMA_DEV_TO_MEM : DMA_MEM_TO_DEV,
|
||||
DMA_PREP_INTERRUPT);
|
||||
if (!tx) {
|
||||
dev_err(nfc->dev, "Could not prepare DMA S/G list\n");
|
||||
dma_unmap_sg(nfc->dma_chan->device->dev, &sg, 1, direction);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
@ -56,6 +57,9 @@
|
||||
|
||||
#define NFC_RB_IRQ_EN BIT(21)
|
||||
|
||||
#define CLK_DIV_SHIFT 0
|
||||
#define CLK_DIV_WIDTH 6
|
||||
|
||||
#define CMDRWGEN(cmd_dir, ran, bch, short_mode, page_size, pages) \
|
||||
( \
|
||||
(cmd_dir) | \
|
||||
@ -151,15 +155,15 @@ struct meson_nfc {
|
||||
struct nand_controller controller;
|
||||
struct clk *core_clk;
|
||||
struct clk *device_clk;
|
||||
struct clk *phase_tx;
|
||||
struct clk *phase_rx;
|
||||
struct clk *nand_clk;
|
||||
struct clk_divider nand_divider;
|
||||
|
||||
unsigned long clk_rate;
|
||||
u32 bus_timing;
|
||||
|
||||
struct device *dev;
|
||||
void __iomem *reg_base;
|
||||
struct regmap *reg_clk;
|
||||
void __iomem *reg_clk;
|
||||
struct completion completion;
|
||||
struct list_head chips;
|
||||
const struct meson_nfc_data *data;
|
||||
@ -235,7 +239,7 @@ static void meson_nfc_select_chip(struct nand_chip *nand, int chip)
|
||||
nfc->timing.tbers_max = meson_chip->tbers_max;
|
||||
|
||||
if (nfc->clk_rate != meson_chip->clk_rate) {
|
||||
ret = clk_set_rate(nfc->device_clk, meson_chip->clk_rate);
|
||||
ret = clk_set_rate(nfc->nand_clk, meson_chip->clk_rate);
|
||||
if (ret) {
|
||||
dev_err(nfc->dev, "failed to set clock rate\n");
|
||||
return;
|
||||
@ -454,7 +458,7 @@ static int meson_nfc_ecc_correct(struct nand_chip *nand, u32 *bitflips,
|
||||
if (ECC_ERR_CNT(*info) != ECC_UNCORRECTABLE) {
|
||||
mtd->ecc_stats.corrected += ECC_ERR_CNT(*info);
|
||||
*bitflips = max_t(u32, *bitflips, ECC_ERR_CNT(*info));
|
||||
*correct_bitmap |= 1 >> i;
|
||||
*correct_bitmap |= BIT_ULL(i);
|
||||
continue;
|
||||
}
|
||||
if ((nand->options & NAND_NEED_SCRAMBLING) &&
|
||||
@ -800,7 +804,7 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
|
||||
u8 *data = buf + i * ecc->size;
|
||||
u8 *oob = nand->oob_poi + i * (ecc->bytes + 2);
|
||||
|
||||
if (correct_bitmap & (1 << i))
|
||||
if (correct_bitmap & BIT_ULL(i))
|
||||
continue;
|
||||
ret = nand_check_erased_ecc_chunk(data, ecc->size,
|
||||
oob, ecc->bytes + 2,
|
||||
@ -987,6 +991,8 @@ static const struct mtd_ooblayout_ops meson_ooblayout_ops = {
|
||||
|
||||
static int meson_nfc_clk_init(struct meson_nfc *nfc)
|
||||
{
|
||||
struct clk_parent_data nfc_divider_parent_data[1];
|
||||
struct clk_init_data init = {0};
|
||||
int ret;
|
||||
|
||||
/* request core clock */
|
||||
@ -1002,21 +1008,28 @@ static int meson_nfc_clk_init(struct meson_nfc *nfc)
|
||||
return PTR_ERR(nfc->device_clk);
|
||||
}
|
||||
|
||||
nfc->phase_tx = devm_clk_get(nfc->dev, "tx");
|
||||
if (IS_ERR(nfc->phase_tx)) {
|
||||
dev_err(nfc->dev, "failed to get TX clk\n");
|
||||
return PTR_ERR(nfc->phase_tx);
|
||||
}
|
||||
init.name = devm_kasprintf(nfc->dev,
|
||||
GFP_KERNEL, "%s#div",
|
||||
dev_name(nfc->dev));
|
||||
init.ops = &clk_divider_ops;
|
||||
nfc_divider_parent_data[0].fw_name = "device";
|
||||
init.parent_data = nfc_divider_parent_data;
|
||||
init.num_parents = 1;
|
||||
nfc->nand_divider.reg = nfc->reg_clk;
|
||||
nfc->nand_divider.shift = CLK_DIV_SHIFT;
|
||||
nfc->nand_divider.width = CLK_DIV_WIDTH;
|
||||
nfc->nand_divider.hw.init = &init;
|
||||
nfc->nand_divider.flags = CLK_DIVIDER_ONE_BASED |
|
||||
CLK_DIVIDER_ROUND_CLOSEST |
|
||||
CLK_DIVIDER_ALLOW_ZERO;
|
||||
|
||||
nfc->phase_rx = devm_clk_get(nfc->dev, "rx");
|
||||
if (IS_ERR(nfc->phase_rx)) {
|
||||
dev_err(nfc->dev, "failed to get RX clk\n");
|
||||
return PTR_ERR(nfc->phase_rx);
|
||||
}
|
||||
nfc->nand_clk = devm_clk_register(nfc->dev, &nfc->nand_divider.hw);
|
||||
if (IS_ERR(nfc->nand_clk))
|
||||
return PTR_ERR(nfc->nand_clk);
|
||||
|
||||
/* init SD_EMMC_CLOCK to sane defaults w/min clock rate */
|
||||
regmap_update_bits(nfc->reg_clk,
|
||||
0, CLK_SELECT_NAND, CLK_SELECT_NAND);
|
||||
writel(CLK_SELECT_NAND | readl(nfc->reg_clk),
|
||||
nfc->reg_clk);
|
||||
|
||||
ret = clk_prepare_enable(nfc->core_clk);
|
||||
if (ret) {
|
||||
@ -1030,29 +1043,21 @@ static int meson_nfc_clk_init(struct meson_nfc *nfc)
|
||||
goto err_device_clk;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(nfc->phase_tx);
|
||||
ret = clk_prepare_enable(nfc->nand_clk);
|
||||
if (ret) {
|
||||
dev_err(nfc->dev, "failed to enable TX clock\n");
|
||||
goto err_phase_tx;
|
||||
dev_err(nfc->dev, "pre enable NFC divider fail\n");
|
||||
goto err_nand_clk;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(nfc->phase_rx);
|
||||
if (ret) {
|
||||
dev_err(nfc->dev, "failed to enable RX clock\n");
|
||||
goto err_phase_rx;
|
||||
}
|
||||
|
||||
ret = clk_set_rate(nfc->device_clk, 24000000);
|
||||
ret = clk_set_rate(nfc->nand_clk, 24000000);
|
||||
if (ret)
|
||||
goto err_disable_rx;
|
||||
goto err_disable_clk;
|
||||
|
||||
return 0;
|
||||
|
||||
err_disable_rx:
|
||||
clk_disable_unprepare(nfc->phase_rx);
|
||||
err_phase_rx:
|
||||
clk_disable_unprepare(nfc->phase_tx);
|
||||
err_phase_tx:
|
||||
err_disable_clk:
|
||||
clk_disable_unprepare(nfc->nand_clk);
|
||||
err_nand_clk:
|
||||
clk_disable_unprepare(nfc->device_clk);
|
||||
err_device_clk:
|
||||
clk_disable_unprepare(nfc->core_clk);
|
||||
@ -1061,8 +1066,7 @@ err_device_clk:
|
||||
|
||||
static void meson_nfc_disable_clk(struct meson_nfc *nfc)
|
||||
{
|
||||
clk_disable_unprepare(nfc->phase_rx);
|
||||
clk_disable_unprepare(nfc->phase_tx);
|
||||
clk_disable_unprepare(nfc->nand_clk);
|
||||
clk_disable_unprepare(nfc->device_clk);
|
||||
clk_disable_unprepare(nfc->core_clk);
|
||||
}
|
||||
@ -1368,7 +1372,6 @@ static int meson_nfc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct meson_nfc *nfc;
|
||||
struct resource *res;
|
||||
int ret, irq;
|
||||
|
||||
nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
|
||||
@ -1385,18 +1388,13 @@ static int meson_nfc_probe(struct platform_device *pdev)
|
||||
|
||||
nfc->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
nfc->reg_base = devm_ioremap_resource(dev, res);
|
||||
nfc->reg_base = devm_platform_ioremap_resource_byname(pdev, "nfc");
|
||||
if (IS_ERR(nfc->reg_base))
|
||||
return PTR_ERR(nfc->reg_base);
|
||||
|
||||
nfc->reg_clk =
|
||||
syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
"amlogic,mmc-syscon");
|
||||
if (IS_ERR(nfc->reg_clk)) {
|
||||
dev_err(dev, "Failed to lookup clock base\n");
|
||||
nfc->reg_clk = devm_platform_ioremap_resource_byname(pdev, "emmc");
|
||||
if (IS_ERR(nfc->reg_clk))
|
||||
return PTR_ERR(nfc->reg_clk);
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
|
@ -335,8 +335,6 @@ static int nand_isbad_bbm(struct nand_chip *chip, loff_t ofs)
|
||||
* @chip: NAND chip structure
|
||||
*
|
||||
* Lock the device and its controller for exclusive access
|
||||
*
|
||||
* Return: -EBUSY if the chip has been suspended, 0 otherwise
|
||||
*/
|
||||
static void nand_get_device(struct nand_chip *chip)
|
||||
{
|
||||
@ -3818,6 +3816,7 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct mtd_ecc_stats old_stats;
|
||||
int ret;
|
||||
|
||||
ops->retlen = 0;
|
||||
@ -3829,11 +3828,20 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
|
||||
nand_get_device(chip);
|
||||
|
||||
old_stats = mtd->ecc_stats;
|
||||
|
||||
if (!ops->datbuf)
|
||||
ret = nand_do_read_oob(chip, from, ops);
|
||||
else
|
||||
ret = nand_do_read_ops(chip, from, ops);
|
||||
|
||||
if (ops->stats) {
|
||||
ops->stats->uncorrectable_errors +=
|
||||
mtd->ecc_stats.failed - old_stats.failed;
|
||||
ops->stats->corrected_bitflips +=
|
||||
mtd->ecc_stats.corrected - old_stats.corrected;
|
||||
}
|
||||
|
||||
nand_release_device(chip);
|
||||
return ret;
|
||||
}
|
||||
@ -5331,11 +5339,10 @@ static int of_get_nand_secure_regions(struct nand_chip *chip)
|
||||
int rawnand_dt_parse_gpio_cs(struct device *dev, struct gpio_desc ***cs_array,
|
||||
unsigned int *ncs_array)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
struct gpio_desc **descs;
|
||||
int ndescs, i;
|
||||
|
||||
ndescs = of_gpio_named_count(np, "cs-gpios");
|
||||
ndescs = gpiod_count(dev, "cs");
|
||||
if (ndescs < 0) {
|
||||
dev_dbg(dev, "No valid cs-gpios property\n");
|
||||
return 0;
|
||||
|
@ -313,7 +313,7 @@ static int scan_read_oob(struct nand_chip *this, uint8_t *buf, loff_t offs,
|
||||
size_t len)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(this);
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
int res, ret = 0;
|
||||
|
||||
ops.mode = MTD_OPS_PLACE_OOB;
|
||||
@ -354,7 +354,7 @@ static int scan_write_bbt(struct nand_chip *this, loff_t offs, size_t len,
|
||||
uint8_t *buf, uint8_t *oob)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(this);
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
|
||||
ops.mode = MTD_OPS_PLACE_OOB;
|
||||
ops.ooboffs = 0;
|
||||
@ -416,7 +416,7 @@ static int scan_block_fast(struct nand_chip *this, struct nand_bbt_descr *bd,
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(this);
|
||||
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
int ret, page_offset;
|
||||
|
||||
ops.ooblen = mtd->oobsize;
|
||||
@ -756,7 +756,7 @@ static int write_bbt(struct nand_chip *this, uint8_t *buf,
|
||||
uint8_t rcode = td->reserved_block_code;
|
||||
size_t retlen, len = 0;
|
||||
loff_t to;
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
|
||||
ops.ooblen = mtd->oobsize;
|
||||
ops.ooboffs = 0;
|
||||
|
@ -170,18 +170,11 @@ static int __init orion_nand_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
/* Not all platforms can gate the clock, so it is not
|
||||
an error if the clock does not exists. */
|
||||
info->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(info->clk)) {
|
||||
ret = PTR_ERR(info->clk);
|
||||
if (ret == -ENOENT) {
|
||||
info->clk = NULL;
|
||||
} else {
|
||||
dev_err(&pdev->dev, "failed to get clock!\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
/* Not all platforms can gate the clock, so it is optional. */
|
||||
info->clk = devm_clk_get_optional(&pdev->dev, NULL);
|
||||
if (IS_ERR(info->clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(info->clk),
|
||||
"failed to get clock!\n");
|
||||
|
||||
ret = clk_prepare_enable(info->clk);
|
||||
if (ret) {
|
||||
|
@ -99,7 +99,7 @@ static const struct mtd_ooblayout_ops oob_sm_small_ops = {
|
||||
static int sm_block_markbad(struct nand_chip *chip, loff_t ofs)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
struct sm_oob oob;
|
||||
int ret;
|
||||
|
||||
|
@ -862,8 +862,8 @@ static int stm32_fmc2_nfc_xfer(struct nand_chip *chip, const u8 *buf,
|
||||
|
||||
ret = dma_map_sg(nfc->dev, nfc->dma_data_sg.sgl,
|
||||
eccsteps, dma_data_dir);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (!ret)
|
||||
return -EIO;
|
||||
|
||||
desc_data = dmaengine_prep_slave_sg(dma_ch, nfc->dma_data_sg.sgl,
|
||||
eccsteps, dma_transfer_dir,
|
||||
@ -893,8 +893,10 @@ static int stm32_fmc2_nfc_xfer(struct nand_chip *chip, const u8 *buf,
|
||||
|
||||
ret = dma_map_sg(nfc->dev, nfc->dma_ecc_sg.sgl,
|
||||
eccsteps, dma_data_dir);
|
||||
if (ret < 0)
|
||||
if (!ret) {
|
||||
ret = -EIO;
|
||||
goto err_unmap_data;
|
||||
}
|
||||
|
||||
desc_ecc = dmaengine_prep_slave_sg(nfc->dma_ecc_ch,
|
||||
nfc->dma_ecc_sg.sgl,
|
||||
@ -1799,9 +1801,8 @@ static int stm32_fmc2_nfc_parse_child(struct stm32_fmc2_nfc *nfc,
|
||||
nand->cs_used[i] = cs;
|
||||
}
|
||||
|
||||
nand->wp_gpio = devm_gpiod_get_from_of_node(nfc->dev, dn,
|
||||
"wp-gpios", 0,
|
||||
GPIOD_OUT_HIGH, "wp");
|
||||
nand->wp_gpio = devm_fwnode_gpiod_get(nfc->dev, of_fwnode_handle(dn),
|
||||
"wp", GPIOD_OUT_HIGH, "wp");
|
||||
if (IS_ERR(nand->wp_gpio)) {
|
||||
ret = PTR_ERR(nand->wp_gpio);
|
||||
if (ret != -ENOENT)
|
||||
|
@ -635,6 +635,7 @@ static int spinand_mtd_read(struct mtd_info *mtd, loff_t from,
|
||||
{
|
||||
struct spinand_device *spinand = mtd_to_spinand(mtd);
|
||||
struct nand_device *nand = mtd_to_nanddev(mtd);
|
||||
struct mtd_ecc_stats old_stats;
|
||||
unsigned int max_bitflips = 0;
|
||||
struct nand_io_iter iter;
|
||||
bool disable_ecc = false;
|
||||
@ -646,6 +647,8 @@ static int spinand_mtd_read(struct mtd_info *mtd, loff_t from,
|
||||
|
||||
mutex_lock(&spinand->lock);
|
||||
|
||||
old_stats = mtd->ecc_stats;
|
||||
|
||||
nanddev_io_for_each_page(nand, NAND_PAGE_READ, from, ops, &iter) {
|
||||
if (disable_ecc)
|
||||
iter.req.mode = MTD_OPS_RAW;
|
||||
@ -668,6 +671,13 @@ static int spinand_mtd_read(struct mtd_info *mtd, loff_t from,
|
||||
ops->oobretlen += iter.req.ooblen;
|
||||
}
|
||||
|
||||
if (ops->stats) {
|
||||
ops->stats->uncorrectable_errors +=
|
||||
mtd->ecc_stats.failed - old_stats.failed;
|
||||
ops->stats->corrected_bitflips +=
|
||||
mtd->ecc_stats.corrected - old_stats.corrected;
|
||||
}
|
||||
|
||||
mutex_unlock(&spinand->lock);
|
||||
|
||||
if (ecc_failed && !ret)
|
||||
|
@ -124,7 +124,7 @@ int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
|
||||
size_t *retlen, uint8_t *buf)
|
||||
{
|
||||
loff_t mask = mtd->writesize - 1;
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
int res;
|
||||
|
||||
ops.mode = MTD_OPS_PLACE_OOB;
|
||||
@ -145,7 +145,7 @@ int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
|
||||
size_t *retlen, uint8_t *buf)
|
||||
{
|
||||
loff_t mask = mtd->writesize - 1;
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
int res;
|
||||
|
||||
ops.mode = MTD_OPS_PLACE_OOB;
|
||||
@ -168,7 +168,7 @@ static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
|
||||
size_t *retlen, uint8_t *buf, uint8_t *oob)
|
||||
{
|
||||
loff_t mask = mtd->writesize - 1;
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
int res;
|
||||
|
||||
ops.mode = MTD_OPS_PLACE_OOB;
|
||||
|
@ -20,6 +20,16 @@ config MTD_BCM63XX_PARTS
|
||||
This provides partition parsing for BCM63xx devices with CFE
|
||||
bootloaders.
|
||||
|
||||
config MTD_BRCM_U_BOOT
|
||||
tristate "Broadcom's U-Boot partition parser"
|
||||
depends on ARCH_BCM4908 || 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.
|
||||
It's possible to locate them by looking for a custom header with a
|
||||
magic value. This driver does that and creates subpartitions for
|
||||
each found environment variables block.
|
||||
|
||||
config MTD_CMDLINE_PARTS
|
||||
tristate "Command line partition table parsing"
|
||||
depends on MTD
|
||||
|
@ -2,6 +2,7 @@
|
||||
obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o
|
||||
obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o
|
||||
obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o
|
||||
obj-$(CONFIG_MTD_BRCM_U_BOOT) += brcm_u-boot.o
|
||||
obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
|
||||
obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o
|
||||
ofpart-y += ofpart_core.o
|
||||
|
84
drivers/mtd/parsers/brcm_u-boot.c
Normal file
84
drivers/mtd/parsers/brcm_u-boot.c
Normal file
@ -0,0 +1,84 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright © 2022 Rafał Miłecki <rafal@milecki.pl>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#define BRCM_U_BOOT_MAX_OFFSET 0x200000
|
||||
#define BRCM_U_BOOT_STEP 0x1000
|
||||
|
||||
#define BRCM_U_BOOT_MAX_PARTS 2
|
||||
|
||||
#define BRCM_U_BOOT_MAGIC 0x75456e76 /* uEnv */
|
||||
|
||||
struct brcm_u_boot_header {
|
||||
__le32 magic;
|
||||
__le32 length;
|
||||
} __packed;
|
||||
|
||||
static const char *names[BRCM_U_BOOT_MAX_PARTS] = {
|
||||
"u-boot-env",
|
||||
"u-boot-env-backup",
|
||||
};
|
||||
|
||||
static int brcm_u_boot_parse(struct mtd_info *mtd,
|
||||
const struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
struct brcm_u_boot_header header;
|
||||
struct mtd_partition *parts;
|
||||
size_t bytes_read;
|
||||
size_t offset;
|
||||
int err;
|
||||
int i = 0;
|
||||
|
||||
parts = kcalloc(BRCM_U_BOOT_MAX_PARTS, sizeof(*parts), GFP_KERNEL);
|
||||
if (!parts)
|
||||
return -ENOMEM;
|
||||
|
||||
for (offset = 0;
|
||||
offset < min_t(size_t, mtd->size, BRCM_U_BOOT_MAX_OFFSET);
|
||||
offset += BRCM_U_BOOT_STEP) {
|
||||
err = mtd_read(mtd, offset, sizeof(header), &bytes_read, (uint8_t *)&header);
|
||||
if (err && !mtd_is_bitflip(err)) {
|
||||
pr_err("Failed to read from %s at 0x%zx: %d\n", mtd->name, offset, err);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (le32_to_cpu(header.magic) != BRCM_U_BOOT_MAGIC)
|
||||
continue;
|
||||
|
||||
parts[i].name = names[i];
|
||||
parts[i].offset = offset;
|
||||
parts[i].size = sizeof(header) + le32_to_cpu(header.length);
|
||||
i++;
|
||||
pr_info("offset:0x%zx magic:0x%08x BINGO\n", offset, header.magic);
|
||||
|
||||
if (i == BRCM_U_BOOT_MAX_PARTS)
|
||||
break;
|
||||
}
|
||||
|
||||
*pparts = parts;
|
||||
|
||||
return i;
|
||||
};
|
||||
|
||||
static const struct of_device_id brcm_u_boot_of_match_table[] = {
|
||||
{ .compatible = "brcm,u-boot" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, brcm_u_boot_of_match_table);
|
||||
|
||||
static struct mtd_part_parser brcm_u_boot_mtd_parser = {
|
||||
.parse_fn = brcm_u_boot_parse,
|
||||
.name = "brcm_u-boot",
|
||||
.of_match_table = brcm_u_boot_of_match_table,
|
||||
};
|
||||
module_mtd_part_parser(brcm_u_boot_mtd_parser);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
@ -193,7 +193,7 @@ static struct mtd_partition * newpart(char *s,
|
||||
parts[this_part].mask_flags = mask_flags;
|
||||
parts[this_part].add_flags = add_flags;
|
||||
if (name)
|
||||
strlcpy(extra_mem, name, name_len + 1);
|
||||
strscpy(extra_mem, name, name_len + 1);
|
||||
else
|
||||
sprintf(extra_mem, "Partition_%03d", this_part);
|
||||
parts[this_part].name = extra_mem;
|
||||
@ -298,7 +298,7 @@ static int mtdpart_setup_real(char *s)
|
||||
this_mtd->parts = parts;
|
||||
this_mtd->num_parts = num_parts;
|
||||
this_mtd->mtd_id = (char*)(this_mtd + 1);
|
||||
strlcpy(this_mtd->mtd_id, mtd_id, mtd_id_len + 1);
|
||||
strscpy(this_mtd->mtd_id, mtd_id, mtd_id_len + 1);
|
||||
|
||||
/* link into chain */
|
||||
this_mtd->next = partitions;
|
||||
|
@ -239,7 +239,7 @@ static int sm_read_sector(struct sm_ftl *ftl,
|
||||
uint8_t *buffer, struct sm_oob *oob)
|
||||
{
|
||||
struct mtd_info *mtd = ftl->trans->mtd;
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
struct sm_oob tmp_oob;
|
||||
int ret = -EIO;
|
||||
int try = 0;
|
||||
@ -323,7 +323,7 @@ static int sm_write_sector(struct sm_ftl *ftl,
|
||||
int zone, int block, int boffset,
|
||||
uint8_t *buffer, struct sm_oob *oob)
|
||||
{
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
struct mtd_info *mtd = ftl->trans->mtd;
|
||||
int ret;
|
||||
|
||||
|
@ -163,7 +163,7 @@ static int read_physical_sector(struct mtd_info *mtd, uint8_t *sect_buf,
|
||||
/* Read redundancy area (wrapper to MTD_READ_OOB */
|
||||
static int read_raw_oob(struct mtd_info *mtd, loff_t offs, uint8_t *buf)
|
||||
{
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
int ret;
|
||||
|
||||
ops.mode = MTD_OPS_RAW;
|
||||
|
@ -99,7 +99,7 @@ static int write_page(int log)
|
||||
static int rewrite_page(int log)
|
||||
{
|
||||
int err = 0;
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
|
||||
if (log)
|
||||
pr_info("rewrite page\n");
|
||||
|
@ -56,7 +56,7 @@ static void do_vary_offset(void)
|
||||
static int write_eraseblock(int ebnum)
|
||||
{
|
||||
int i;
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
int err = 0;
|
||||
loff_t addr = (loff_t)ebnum * mtd->erasesize;
|
||||
|
||||
@ -165,7 +165,7 @@ static size_t memffshow(loff_t addr, loff_t offset, const void *cs,
|
||||
static int verify_eraseblock(int ebnum)
|
||||
{
|
||||
int i;
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
int err = 0;
|
||||
loff_t addr = (loff_t)ebnum * mtd->erasesize;
|
||||
size_t bitflips;
|
||||
@ -260,7 +260,7 @@ static int verify_eraseblock(int ebnum)
|
||||
|
||||
static int verify_eraseblock_in_one_go(int ebnum)
|
||||
{
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
int err = 0;
|
||||
loff_t addr = (loff_t)ebnum * mtd->erasesize;
|
||||
size_t len = mtd->oobavail * pgcnt;
|
||||
@ -338,7 +338,7 @@ static int __init mtd_oobtest_init(void)
|
||||
int err = 0;
|
||||
unsigned int i;
|
||||
uint64_t tmp;
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
loff_t addr = 0, addr0;
|
||||
|
||||
printk(KERN_INFO "\n");
|
||||
|
@ -47,7 +47,7 @@ static int read_eraseblock_by_page(int ebnum)
|
||||
err = ret;
|
||||
}
|
||||
if (mtd->oobsize) {
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
|
||||
ops.mode = MTD_OPS_PLACE_OOB;
|
||||
ops.len = 0;
|
||||
|
@ -1035,7 +1035,7 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c,
|
||||
{
|
||||
int i, ret;
|
||||
int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
|
||||
ops.mode = MTD_OPS_AUTO_OOB;
|
||||
ops.ooblen = NR_OOB_SCAN_PAGES * c->oobavail;
|
||||
@ -1076,7 +1076,7 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c,
|
||||
int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb)
|
||||
{
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
int ret, cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
|
||||
|
||||
ops.mode = MTD_OPS_AUTO_OOB;
|
||||
@ -1101,7 +1101,7 @@ int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb)
|
||||
{
|
||||
int ret;
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_ops ops = { };
|
||||
int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
|
||||
|
||||
ops.mode = MTD_OPS_AUTO_OOB;
|
||||
|
@ -40,6 +40,12 @@ struct mtd_erase_region_info {
|
||||
unsigned long *lockmap; /* If keeping bitmap of locks */
|
||||
};
|
||||
|
||||
struct mtd_req_stats {
|
||||
unsigned int uncorrectable_errors;
|
||||
unsigned int corrected_bitflips;
|
||||
unsigned int max_bitflips;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mtd_oob_ops - oob operation operands
|
||||
* @mode: operation mode
|
||||
@ -70,6 +76,7 @@ struct mtd_oob_ops {
|
||||
uint32_t ooboffs;
|
||||
uint8_t *datbuf;
|
||||
uint8_t *oobbuf;
|
||||
struct mtd_req_stats *stats;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -55,9 +55,9 @@ struct mtd_oob_buf64 {
|
||||
* @MTD_OPS_RAW: data are transferred as-is, with no error correction;
|
||||
* this mode implies %MTD_OPS_PLACE_OOB
|
||||
*
|
||||
* These modes can be passed to ioctl(MEMWRITE) and are also used internally.
|
||||
* See notes on "MTD file modes" for discussion on %MTD_OPS_RAW vs.
|
||||
* %MTD_FILE_MODE_RAW.
|
||||
* These modes can be passed to ioctl(MEMWRITE) and ioctl(MEMREAD); they are
|
||||
* also used internally. See notes on "MTD file modes" for discussion on
|
||||
* %MTD_OPS_RAW vs. %MTD_FILE_MODE_RAW.
|
||||
*/
|
||||
enum {
|
||||
MTD_OPS_PLACE_OOB = 0,
|
||||
@ -91,6 +91,53 @@ struct mtd_write_req {
|
||||
__u8 padding[7];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mtd_read_req_ecc_stats - ECC statistics for a read operation
|
||||
*
|
||||
* @uncorrectable_errors: the number of uncorrectable errors that happened
|
||||
* during the read operation
|
||||
* @corrected_bitflips: the number of bitflips corrected during the read
|
||||
* operation
|
||||
* @max_bitflips: the maximum number of bitflips detected in any single ECC
|
||||
* step for the data read during the operation; this information
|
||||
* can be used to decide whether the data stored in a specific
|
||||
* region of the MTD device should be moved somewhere else to
|
||||
* avoid data loss.
|
||||
*/
|
||||
struct mtd_read_req_ecc_stats {
|
||||
__u32 uncorrectable_errors;
|
||||
__u32 corrected_bitflips;
|
||||
__u32 max_bitflips;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mtd_read_req - data structure for requesting a read operation
|
||||
*
|
||||
* @start: start address
|
||||
* @len: length of data buffer (only lower 32 bits are used)
|
||||
* @ooblen: length of OOB buffer (only lower 32 bits are used)
|
||||
* @usr_data: user-provided data buffer
|
||||
* @usr_oob: user-provided OOB buffer
|
||||
* @mode: MTD mode (see "MTD operation modes")
|
||||
* @padding: reserved, must be set to 0
|
||||
* @ecc_stats: ECC statistics for the read operation
|
||||
*
|
||||
* This structure supports ioctl(MEMREAD) operations, allowing data and/or OOB
|
||||
* reads in various modes. To read from OOB-only, set @usr_data == NULL, and to
|
||||
* read data-only, set @usr_oob == NULL. However, setting both @usr_data and
|
||||
* @usr_oob to NULL is not allowed.
|
||||
*/
|
||||
struct mtd_read_req {
|
||||
__u64 start;
|
||||
__u64 len;
|
||||
__u64 ooblen;
|
||||
__u64 usr_data;
|
||||
__u64 usr_oob;
|
||||
__u8 mode;
|
||||
__u8 padding[7];
|
||||
struct mtd_read_req_ecc_stats ecc_stats;
|
||||
};
|
||||
|
||||
#define MTD_ABSENT 0
|
||||
#define MTD_RAM 1
|
||||
#define MTD_ROM 2
|
||||
@ -207,6 +254,12 @@ struct otp_info {
|
||||
#define MEMWRITE _IOWR('M', 24, struct mtd_write_req)
|
||||
/* Erase a given range of user data (must be in mode %MTD_FILE_MODE_OTP_USER) */
|
||||
#define OTPERASE _IOW('M', 25, struct otp_info)
|
||||
/*
|
||||
* Most generic read interface; can read in-band and/or out-of-band in various
|
||||
* modes (see "struct mtd_read_req"). This ioctl is not supported for flashes
|
||||
* without OOB, e.g., NOR flash.
|
||||
*/
|
||||
#define MEMREAD _IOWR('M', 26, struct mtd_read_req)
|
||||
|
||||
/*
|
||||
* Obsolete legacy interface. Keep it in order not to break userspace
|
||||
@ -270,8 +323,9 @@ struct mtd_ecc_stats {
|
||||
* Note: %MTD_FILE_MODE_RAW provides the same functionality as %MTD_OPS_RAW -
|
||||
* raw access to the flash, without error correction or autoplacement schemes.
|
||||
* Wherever possible, the MTD_OPS_* mode will override the MTD_FILE_MODE_* mode
|
||||
* (e.g., when using ioctl(MEMWRITE)), but in some cases, the MTD_FILE_MODE is
|
||||
* used out of necessity (e.g., `write()', ioctl(MEMWRITEOOB64)).
|
||||
* (e.g., when using ioctl(MEMWRITE) or ioctl(MEMREAD)), but in some cases, the
|
||||
* MTD_FILE_MODE is used out of necessity (e.g., `write()',
|
||||
* ioctl(MEMWRITEOOB64)).
|
||||
*/
|
||||
enum mtd_file_modes {
|
||||
MTD_FILE_MODE_NORMAL = MTD_OTP_OFF,
|
||||
|
Loading…
Reference in New Issue
Block a user