Merge tag 'nand/for-7.1' into mtd/next

The main changes happened in the SunXi driver in order to
support new versions of the Allwinner NAND controller.

There are also some DT-binding improvements and cleanups.

Finally a couple of actual fixes (Realtek ECC and Winbond SPI NAND),
aside with the usual load of misc changes.
This commit is contained in:
Miquel Raynal
2026-04-17 21:51:05 +02:00
17 changed files with 616 additions and 240 deletions

View File

@@ -101,7 +101,7 @@ required:
unevaluatedProperties: false
allOf:
- $ref: nand-controller.yaml
- $ref: nand-controller-legacy.yaml
- if:
properties:

View File

@@ -10,22 +10,43 @@ maintainers:
- Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
allOf:
- $ref: nand-controller.yaml
- $ref: nand-controller-legacy.yaml
properties:
compatible:
oneOf:
- const: fsl,imx27-nand
- enum:
- fsl,imx25-nand
- fsl,imx27-nand
- fsl,imx51-nand
- fsl,imx53-nand
- items:
- enum:
- fsl,imx35-nand
- const: fsl,imx25-nand
- items:
- enum:
- fsl,imx31-nand
- const: fsl,imx27-nand
reg:
maxItems: 1
minItems: 1
items:
- description: IP register space
- description: Nand flash internal buffer space
interrupts:
maxItems: 1
clocks:
maxItems: 1
dmas:
maxItems: 1
dma-names:
items:
- const: rx-tx
required:
- compatible
- reg

View File

@@ -11,6 +11,7 @@ maintainers:
allOf:
- $ref: mtd.yaml#
- $ref: nand-property.yaml
description: |
This file covers the generic description of a NAND chip. It implies that the
@@ -22,51 +23,6 @@ properties:
description:
Contains the chip-select IDs.
nand-ecc-engine:
description: |
A phandle on the hardware ECC engine if any. There are
basically three possibilities:
1/ The ECC engine is part of the NAND controller, in this
case the phandle should reference the parent node.
2/ The ECC engine is part of the NAND part (on-die), in this
case the phandle should reference the node itself.
3/ The ECC engine is external, in this case the phandle should
reference the specific ECC engine node.
$ref: /schemas/types.yaml#/definitions/phandle
nand-use-soft-ecc-engine:
description: Use a software ECC engine.
type: boolean
nand-no-ecc-engine:
description: Do not use any ECC correction.
type: boolean
nand-ecc-algo:
description:
Desired ECC algorithm.
$ref: /schemas/types.yaml#/definitions/string
enum: [hamming, bch, rs]
nand-ecc-strength:
description:
Maximum number of bits that can be corrected per ECC step.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 1
nand-ecc-step-size:
description:
Number of data bytes covered by a single ECC step.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 1
secure-regions:
description:
Regions in the NAND chip which are protected using a secure element
like Trustzone. This property contains the start address and size of
the secure regions present.
$ref: /schemas/types.yaml#/definitions/uint64-matrix
required:
- reg

View File

@@ -0,0 +1,65 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mtd/nand-controller-legacy.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NAND Controller Common Properties
maintainers:
- Miquel Raynal <miquel.raynal@bootlin.com>
- Richard Weinberger <richard@nod.at>
description: >
The NAND controller should be represented with its own DT node, and
all NAND chips attached to this controller should be defined as
children nodes of the NAND controller. This representation should be
enforced even for simple controllers supporting only one chip.
This is only for legacy nand controller, new controller should use
nand-controller.yaml
properties:
"#address-cells":
const: 1
"#size-cells":
enum: [0, 1]
ranges: true
cs-gpios:
description:
Array of chip-select available to the controller. The first
entries are a 1:1 mapping of the available chip-select on the
NAND controller (even if they are not used). As many additional
chip-select as needed may follow and should be phandles of GPIO
lines. 'reg' entries of the NAND chip subnodes become indexes of
this array when this property is present.
minItems: 1
maxItems: 8
partitions:
type: object
required:
- compatible
patternProperties:
"^nand@[a-f0-9]$":
type: object
$ref: raw-nand-chip.yaml#
"^partition@[0-9a-f]+$":
type: object
$ref: /schemas/mtd/partitions/partition.yaml#/$defs/partition-node
deprecated: true
allOf:
- $ref: raw-nand-property.yaml#
- $ref: nand-property.yaml#
# This is a generic file other binding inherit from and extend
additionalProperties: true

View File

@@ -16,6 +16,8 @@ description: |
children nodes of the NAND controller. This representation should be
enforced even for simple controllers supporting only one chip.
select: false
properties:
$nodename:
pattern: "^nand-controller(@.*)?"

View File

@@ -0,0 +1,64 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mtd/nand-property.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NAND Chip Common Properties
maintainers:
- Miquel Raynal <miquel.raynal@bootlin.com>
description: |
This file covers the generic properties of a NAND chip. It implies that the
bus interface should not be taken into account: both raw NAND devices and
SPI-NAND devices are concerned by this description.
properties:
nand-ecc-engine:
description: |
A phandle on the hardware ECC engine if any. There are
basically three possibilities:
1/ The ECC engine is part of the NAND controller, in this
case the phandle should reference the parent node.
2/ The ECC engine is part of the NAND part (on-die), in this
case the phandle should reference the node itself.
3/ The ECC engine is external, in this case the phandle should
reference the specific ECC engine node.
$ref: /schemas/types.yaml#/definitions/phandle
nand-use-soft-ecc-engine:
description: Use a software ECC engine.
type: boolean
nand-no-ecc-engine:
description: Do not use any ECC correction.
type: boolean
nand-ecc-algo:
description:
Desired ECC algorithm.
$ref: /schemas/types.yaml#/definitions/string
enum: [hamming, bch, rs]
nand-ecc-strength:
description:
Maximum number of bits that can be corrected per ECC step.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 1
nand-ecc-step-size:
description:
Number of data bytes covered by a single ECC step.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 1
secure-regions:
description:
Regions in the NAND chip which are protected using a secure element
like Trustzone. This property contains the start address and size of
the secure regions present.
$ref: /schemas/types.yaml#/definitions/uint64-matrix
# This file can be referenced by more specific devices (like spi-nands)
additionalProperties: true

View File

@@ -11,6 +11,7 @@ maintainers:
allOf:
- $ref: nand-chip.yaml#
- $ref: raw-nand-property.yaml#
description: |
The ECC strength and ECC step size properties define the user
@@ -31,79 +32,6 @@ properties:
description:
Contains the chip-select IDs.
nand-ecc-placement:
description:
Location of the ECC bytes. This location is unknown by default
but can be explicitly set to "oob", if all ECC bytes are
known to be stored in the OOB area, or "interleaved" if ECC
bytes will be interleaved with regular data in the main area.
$ref: /schemas/types.yaml#/definitions/string
enum: [ oob, interleaved ]
deprecated: true
nand-ecc-mode:
description:
Legacy ECC configuration mixing the ECC engine choice and
configuration.
$ref: /schemas/types.yaml#/definitions/string
enum: [none, soft, soft_bch, hw, hw_syndrome, on-die]
deprecated: true
nand-bus-width:
description:
Bus width to the NAND chip
$ref: /schemas/types.yaml#/definitions/uint32
enum: [8, 16]
default: 8
nand-on-flash-bbt:
description:
With this property, the OS will search the device for a Bad
Block Table (BBT). If not found, it will create one, reserve
a few blocks at the end of the device to store it and update
it as the device ages. Otherwise, the out-of-band area of a
few pages of all the blocks will be scanned at boot time to
find Bad Block Markers (BBM). These markers will help to
build a volatile BBT in RAM.
$ref: /schemas/types.yaml#/definitions/flag
nand-ecc-maximize:
description:
Whether or not the ECC strength should be maximized. The
maximum ECC strength is both controller and chip
dependent. The ECC engine has to select the ECC config
providing the best strength and taking the OOB area size
constraint into account. This is particularly useful when
only the in-band area is used by the upper layers, and you
want to make your NAND as reliable as possible.
$ref: /schemas/types.yaml#/definitions/flag
nand-is-boot-medium:
description:
Whether or not the NAND chip is a boot medium. Drivers might
use this information to select ECC algorithms supported by
the boot ROM or similar restrictions.
$ref: /schemas/types.yaml#/definitions/flag
nand-rb:
description:
Contains the native Ready/Busy IDs.
$ref: /schemas/types.yaml#/definitions/uint32-array
rb-gpios:
description:
Contains one or more GPIO descriptor (the numper of descriptor
depends on the number of R/B pins exposed by the flash) for the
Ready/Busy pins. Active state refers to the NAND ready state and
should be set to GPIOD_ACTIVE_HIGH unless the signal is inverted.
wp-gpios:
description:
Contains one GPIO descriptor for the Write Protect pin.
Active state refers to the NAND Write Protect state and should be
set to GPIOD_ACTIVE_LOW unless the signal is inverted.
maxItems: 1
required:
- reg

View File

@@ -0,0 +1,98 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mtd/raw-nand-property.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Raw NAND Chip Common Properties
maintainers:
- Miquel Raynal <miquel.raynal@bootlin.com>
description: |
The ECC strength and ECC step size properties define the user
desires in terms of correction capability of a controller. Together,
they request the ECC engine to correct {strength} bit errors per
{size} bytes for a particular raw NAND chip.
The interpretation of these parameters is implementation-defined, so
not all implementations must support all possible
combinations. However, implementations are encouraged to further
specify the value(s) they support.
properties:
nand-ecc-placement:
description:
Location of the ECC bytes. This location is unknown by default
but can be explicitly set to "oob", if all ECC bytes are
known to be stored in the OOB area, or "interleaved" if ECC
bytes will be interleaved with regular data in the main area.
$ref: /schemas/types.yaml#/definitions/string
enum: [ oob, interleaved ]
deprecated: true
nand-ecc-mode:
description:
Legacy ECC configuration mixing the ECC engine choice and
configuration.
$ref: /schemas/types.yaml#/definitions/string
enum: [none, soft, soft_bch, hw, hw_syndrome, on-die]
deprecated: true
nand-bus-width:
description:
Bus width to the NAND chip
$ref: /schemas/types.yaml#/definitions/uint32
enum: [8, 16]
default: 8
nand-on-flash-bbt:
description:
With this property, the OS will search the device for a Bad
Block Table (BBT). If not found, it will create one, reserve
a few blocks at the end of the device to store it and update
it as the device ages. Otherwise, the out-of-band area of a
few pages of all the blocks will be scanned at boot time to
find Bad Block Markers (BBM). These markers will help to
build a volatile BBT in RAM.
$ref: /schemas/types.yaml#/definitions/flag
nand-ecc-maximize:
description:
Whether or not the ECC strength should be maximized. The
maximum ECC strength is both controller and chip
dependent. The ECC engine has to select the ECC config
providing the best strength and taking the OOB area size
constraint into account. This is particularly useful when
only the in-band area is used by the upper layers, and you
want to make your NAND as reliable as possible.
$ref: /schemas/types.yaml#/definitions/flag
nand-is-boot-medium:
description:
Whether or not the NAND chip is a boot medium. Drivers might
use this information to select ECC algorithms supported by
the boot ROM or similar restrictions.
$ref: /schemas/types.yaml#/definitions/flag
nand-rb:
description:
Contains the native Ready/Busy IDs.
$ref: /schemas/types.yaml#/definitions/uint32-array
rb-gpios:
description:
Contains one or more GPIO descriptor (the numper of descriptor
depends on the number of R/B pins exposed by the flash) for the
Ready/Busy pins. Active state refers to the NAND ready state and
should be set to GPIOD_ACTIVE_HIGH unless the signal is inverted.
wp-gpios:
description:
Contains one GPIO descriptor for the Write Protect pin.
Active state refers to the NAND Write Protect state and should be
set to GPIOD_ACTIVE_LOW unless the signal is inverted.
maxItems: 1
# This is a generic file other binding inherit from and extend
additionalProperties: true

View File

@@ -17,10 +17,12 @@
* - BCH12 : Generate 20 ECC bytes from 512 data bytes plus 6 free bytes
*
* It can run for arbitrary NAND flash chips with different block and OOB sizes. Currently there
* are only two known devices in the wild that have NAND flash and make use of this ECC engine
* (Linksys LGS328C & LGS352C). To keep compatibility with vendor firmware, new modes can only
* be added when new data layouts have been analyzed. For now allow BCH6 on flash with 2048 byte
* blocks and 64 bytes oob.
* are a few known devices in the wild that make use of this ECC engine
* (Linksys LGS328C, LGS352C & Netlink HG323DAC). To keep compatibility with vendor firmware,
* new modes can only be added when new data layouts have been analyzed. For now allow BCH6 on
* flash with 2048 byte blocks and at least 64 bytes oob. Some vendors make use of
* 128 bytes OOB NAND chips (e.g. Macronix MX35LF1G24AD) but only use BCH6 and thus the first
* 64 bytes of the OOB area. In this case the engine leaves any extra bytes unused.
*
* This driver aligns with kernel ECC naming conventions. Neverthless a short notice on the
* Realtek naming conventions for the different structures in the OOB area.
@@ -39,7 +41,7 @@
*/
#define RTL_ECC_ALLOWED_PAGE_SIZE 2048
#define RTL_ECC_ALLOWED_OOB_SIZE 64
#define RTL_ECC_ALLOWED_MIN_OOB_SIZE 64
#define RTL_ECC_ALLOWED_STRENGTH 6
#define RTL_ECC_BLOCK_SIZE 512
@@ -310,10 +312,10 @@ static int rtl_ecc_check_support(struct nand_device *nand)
struct mtd_info *mtd = nanddev_to_mtd(nand);
struct device *dev = nand->ecc.engine->dev;
if (mtd->oobsize != RTL_ECC_ALLOWED_OOB_SIZE ||
if (mtd->oobsize < RTL_ECC_ALLOWED_MIN_OOB_SIZE ||
mtd->writesize != RTL_ECC_ALLOWED_PAGE_SIZE) {
dev_err(dev, "only flash geometry data=%d, oob=%d supported\n",
RTL_ECC_ALLOWED_PAGE_SIZE, RTL_ECC_ALLOWED_OOB_SIZE);
dev_err(dev, "only flash geometry data=%d, oob>=%d supported\n",
RTL_ECC_ALLOWED_PAGE_SIZE, RTL_ECC_ALLOWED_MIN_OOB_SIZE);
return -EINVAL;
}

View File

@@ -837,9 +837,10 @@ static const struct pci_device_id cafe_nand_tbl[] = {
MODULE_DEVICE_TABLE(pci, cafe_nand_tbl);
static int cafe_nand_resume(struct pci_dev *pdev)
static int cafe_nand_resume(struct device *dev)
{
uint32_t ctrl;
struct pci_dev *pdev = to_pci_dev(dev);
struct mtd_info *mtd = pci_get_drvdata(pdev);
struct nand_chip *chip = mtd_to_nand(mtd);
struct cafe_priv *cafe = nand_get_controller_data(chip);
@@ -877,12 +878,14 @@ static int cafe_nand_resume(struct pci_dev *pdev)
return 0;
}
static DEFINE_SIMPLE_DEV_PM_OPS(cafe_nand_ops, NULL, cafe_nand_resume);
static struct pci_driver cafe_nand_pci_driver = {
.name = "CAFÉ NAND",
.id_table = cafe_nand_tbl,
.probe = cafe_nand_probe,
.remove = cafe_nand_remove,
.resume = cafe_nand_resume,
.driver.pm = &cafe_nand_ops,
};
module_pci_driver(cafe_nand_pci_driver);

View File

@@ -7,6 +7,7 @@
* Author: Dipen Dudhat <Dipen.Dudhat@freescale.com>
*/
#include <linux/cleanup.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/types.h>
@@ -863,7 +864,14 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
/* Fill in fsl_ifc_mtd structure */
mtd->dev.parent = priv->dev;
nand_set_flash_node(chip, priv->dev->of_node);
struct device_node *np __free(device_node) =
of_get_next_child_with_prefix(priv->dev->of_node, NULL, "nand");
if (np)
nand_set_flash_node(chip, np);
else
nand_set_flash_node(chip, priv->dev->of_node);
/* fill in nand_chip structure */
/* set up function call table */

View File

@@ -5,6 +5,7 @@
* Copyright (C) 2010-2015 Freescale Semiconductor, Inc.
* Copyright (C) 2008 Embedded Alley Solutions, Inc.
*/
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/slab.h>
@@ -2688,7 +2689,15 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
/* init the nand_chip{}, we don't support a 16-bit NAND Flash bus. */
nand_set_controller_data(chip, this);
nand_set_flash_node(chip, this->pdev->dev.of_node);
struct device_node *np __free(device_node) =
of_get_next_child_with_prefix(this->pdev->dev.of_node, NULL, "nand");
if (np)
nand_set_flash_node(chip, np);
else
nand_set_flash_node(chip, this->pdev->dev.of_node);
chip->legacy.block_markbad = gpmi_block_markbad;
chip->badblock_pattern = &gpmi_bbt_descr;
chip->options |= NAND_NO_SUBPAGE_WRITE;

View File

@@ -4,6 +4,7 @@
* Copyright 2008 Sascha Hauer, kernel@pengutronix.de
*/
#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
@@ -1714,7 +1715,14 @@ static int mxcnd_probe(struct platform_device *pdev)
this->legacy.chip_delay = 5;
nand_set_controller_data(this, host);
nand_set_flash_node(this, pdev->dev.of_node);
struct device_node *np __free(device_node) =
of_get_next_child_with_prefix(pdev->dev.of_node, NULL, "nand");
if (np)
nand_set_flash_node(this, np);
else
nand_set_flash_node(this, pdev->dev.of_node);
host->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(host->clk))

View File

@@ -43,6 +43,7 @@
#include <linux/mtd/partitions.h>
#include <linux/of.h>
#include <linux/gpio/consumer.h>
#include <linux/cleanup.h>
#include "internals.h"
@@ -4704,16 +4705,16 @@ static void nand_resume(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd_to_nand(mtd);
mutex_lock(&chip->lock);
if (chip->suspended) {
if (chip->ops.resume)
chip->ops.resume(chip);
chip->suspended = 0;
} else {
pr_err("%s called for a chip which is not in suspended state\n",
__func__);
scoped_guard(mutex, &chip->lock) {
if (chip->suspended) {
if (chip->ops.resume)
chip->ops.resume(chip);
chip->suspended = 0;
} else {
pr_err("%s called for a chip which is not in suspended state\n",
__func__);
}
}
mutex_unlock(&chip->lock);
wake_up_all(&chip->resume_wq);
}

View File

@@ -209,9 +209,8 @@
/*
* On A10/A23, this is the size of the NDFC User Data Register, containing the
* mandatory user data bytes following the ECC for each ECC step.
* mandatory user data bytes preceding the ECC for each ECC step.
* Thus, for each ECC step, we need the ECC bytes + USER_DATA_SZ.
* Those bits are currently unsused, and kept as default value 0xffffffff.
*
* On H6/H616, this size became configurable, from 0 bytes to 32, via the
* USER_DATA_LEN registers.
@@ -249,6 +248,7 @@ struct sunxi_nand_hw_ecc {
* @timing_ctl: TIMING_CTL register value for this NAND chip
* @nsels: number of CS lines required by the NAND chip
* @sels: array of CS lines descriptions
* @user_data_bytes: array of user data lengths for all ECC steps
*/
struct sunxi_nand_chip {
struct list_head node;
@@ -257,6 +257,7 @@ struct sunxi_nand_chip {
unsigned long clk_rate;
u32 timing_cfg;
u32 timing_ctl;
u8 *user_data_bytes;
int nsels;
struct sunxi_nand_chip_sel sels[] __counted_by(nsels);
};
@@ -272,9 +273,11 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
*
* @has_mdma: Use mbus dma mode, otherwise general dma
* through MBUS on A23/A33 needs extra configuration.
* @has_ecc_block_512: If the ECC can handle 512B or only 1024B chuncks
* @has_ecc_block_512: If the ECC can handle 512B or only 1024B chunks
* @has_ecc_clk: If the controller needs an ECC clock.
* @has_mbus_clk: If the controller needs a mbus clock.
* @legacy_max_strength:If the maximize strength function was off by 2 bytes
* NB: this should not be used in new controllers
* @reg_io_data: I/O data register
* @reg_ecc_err_cnt: ECC error counter register
* @reg_user_data: User data register
@@ -292,7 +295,7 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
* @nstrengths: Size of @ecc_strengths
* @max_ecc_steps: Maximum supported steps for ECC, this is also the
* number of user data registers
* @user_data_len_tab: Table of lenghts supported by USER_DATA_LEN register
* @user_data_len_tab: Table of lengths supported by USER_DATA_LEN register
* The table index is the value to set in NFC_USER_DATA_LEN
* registers, and the corresponding value is the number of
* bytes to write
@@ -304,6 +307,7 @@ struct sunxi_nfc_caps {
bool has_ecc_block_512;
bool has_ecc_clk;
bool has_mbus_clk;
bool legacy_max_strength;
unsigned int reg_io_data;
unsigned int reg_ecc_err_cnt;
unsigned int reg_user_data;
@@ -820,12 +824,50 @@ static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf)
return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
}
static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip *nand, u8 *oob,
int step, bool bbm, int page)
static u8 sunxi_nfc_user_data_sz(struct sunxi_nand_chip *sunxi_nand, int step)
{
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
if (!sunxi_nand->user_data_bytes)
return USER_DATA_SZ;
sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(nfc, step)), oob);
return sunxi_nand->user_data_bytes[step];
}
static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip *nand, u8 *oob,
int step, bool bbm, int page,
unsigned int user_data_sz)
{
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
u32 user_data;
if (!nfc->caps->reg_user_data_len) {
/*
* For A10, the user data for step n is in the nth
* REG_USER_DATA
*/
user_data = readl(nfc->regs + NFC_REG_USER_DATA(nfc, step));
sunxi_nfc_user_data_to_buf(user_data, oob);
} else {
/*
* For H6 NAND controller, the user data for all steps is
* contained in 32 user data registers, but not at a specific
* offset for each step, they are just concatenated.
*/
unsigned int user_data_off = 0;
unsigned int reg_off;
u8 *ptr = oob;
unsigned int i;
for (i = 0; i < step; i++)
user_data_off += sunxi_nfc_user_data_sz(sunxi_nand, i);
user_data_off /= 4;
for (i = 0; i < user_data_sz / 4; i++, ptr += 4) {
reg_off = NFC_REG_USER_DATA(nfc, user_data_off + i);
user_data = readl(nfc->regs + reg_off);
sunxi_nfc_user_data_to_buf(user_data, ptr);
}
}
/* De-randomize the Bad Block Marker. */
if (bbm && (nand->options & NAND_NEED_SCRAMBLING))
@@ -884,17 +926,46 @@ static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand,
bool bbm, int page)
{
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
u8 user_data[USER_DATA_SZ];
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, step);
u8 *user_data = NULL;
/* Randomize the Bad Block Marker. */
if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) {
memcpy(user_data, oob, sizeof(user_data));
user_data = kmalloc(user_data_sz, GFP_KERNEL);
memcpy(user_data, oob, user_data_sz);
sunxi_nfc_randomize_bbm(nand, page, user_data);
oob = user_data;
}
writel(sunxi_nfc_buf_to_user_data(oob),
nfc->regs + NFC_REG_USER_DATA(nfc, step));
if (!nfc->caps->reg_user_data_len) {
/*
* For A10, the user data for step n is in the nth
* REG_USER_DATA
*/
writel(sunxi_nfc_buf_to_user_data(oob),
nfc->regs + NFC_REG_USER_DATA(nfc, step));
} else {
/*
* For H6 NAND controller, the user data for all steps is
* contained in 32 user data registers, but not at a specific
* offset for each step, they are just concatenated.
*/
unsigned int user_data_off = 0;
const u8 *ptr = oob;
unsigned int i;
for (i = 0; i < step; i++)
user_data_off += sunxi_nfc_user_data_sz(sunxi_nand, i);
user_data_off /= 4;
for (i = 0; i < user_data_sz / 4; i++, ptr += 4) {
writel(sunxi_nfc_buf_to_user_data(ptr),
nfc->regs + NFC_REG_USER_DATA(nfc, user_data_off + i));
}
}
kfree(user_data);
}
static void sunxi_nfc_hw_ecc_update_stats(struct nand_chip *nand,
@@ -915,6 +986,8 @@ static int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob,
bool *erased)
{
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, step);
struct nand_ecc_ctrl *ecc = &nand->ecc;
u32 tmp;
@@ -937,7 +1010,7 @@ static int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob,
memset(data, pattern, ecc->size);
if (oob)
memset(oob, pattern, ecc->bytes + USER_DATA_SZ);
memset(oob, pattern, ecc->bytes + user_data_sz);
return 0;
}
@@ -952,14 +1025,19 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
u8 *oob, int oob_off,
int *cur_off,
unsigned int *max_bitflips,
bool bbm, bool oob_required, int page)
int step, bool oob_required, int page)
{
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, step);
struct nand_ecc_ctrl *ecc = &nand->ecc;
int raw_mode = 0;
u32 pattern_found;
bool bbm = !step;
bool erased;
int ret;
/* From the controller point of view, we are at step 0 */
const int nfc_step = 0;
if (*cur_off != data_off)
nand_change_read_column_op(nand, data_off, NULL, 0, false);
@@ -973,8 +1051,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
if (ret)
return ret;
sunxi_nfc_reset_user_data_len(nfc);
sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, 0);
sunxi_nfc_set_user_data_len(nfc, user_data_sz, nfc_step);
sunxi_nfc_randomizer_config(nand, page, false);
sunxi_nfc_randomizer_enable(nand);
writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP,
@@ -985,15 +1062,14 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
if (ret)
return ret;
*cur_off = oob_off + ecc->bytes + USER_DATA_SZ;
*cur_off = oob_off + ecc->bytes + user_data_sz;
pattern_found = readl(nfc->regs + nfc->caps->reg_pat_found);
pattern_found = field_get(NFC_ECC_PAT_FOUND_MSK(nfc), pattern_found);
ret = sunxi_nfc_hw_ecc_correct(nand, data, oob_required ? oob : NULL, 0,
readl(nfc->regs + NFC_REG_ECC_ST),
pattern_found,
&erased);
ret = sunxi_nfc_hw_ecc_correct(nand, data, oob_required ? oob : NULL,
nfc_step, readl(nfc->regs + NFC_REG_ECC_ST),
pattern_found, &erased);
if (erased)
return 1;
@@ -1010,10 +1086,10 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
ecc->size);
nand_change_read_column_op(nand, oob_off, oob,
ecc->bytes + USER_DATA_SZ, false);
ecc->bytes + user_data_sz, false);
ret = nand_check_erased_ecc_chunk(data, ecc->size, oob,
ecc->bytes + USER_DATA_SZ,
ecc->bytes + user_data_sz,
NULL, 0, ecc->strength);
if (ret >= 0)
raw_mode = 1;
@@ -1023,11 +1099,11 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
if (oob_required) {
nand_change_read_column_op(nand, oob_off, NULL, 0,
false);
sunxi_nfc_randomizer_read_buf(nand, oob, ecc->bytes + USER_DATA_SZ,
sunxi_nfc_randomizer_read_buf(nand, oob, ecc->bytes + user_data_sz,
true, page);
sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, 0,
bbm, page);
sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, nfc_step,
bbm, page, user_data_sz);
}
}
@@ -1036,21 +1112,50 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
return raw_mode;
}
/*
* Returns the offset of the OOB for each step.
* (it includes the user data before the ECC data.)
*/
static int sunxi_get_oob_offset(struct sunxi_nand_chip *sunxi_nand,
struct nand_ecc_ctrl *ecc, int step)
{
int ecc_off = step * ecc->bytes;
int i;
for (i = 0; i < step; i++)
ecc_off += sunxi_nfc_user_data_sz(sunxi_nand, i);
return ecc_off;
}
/*
* Returns the offset of the ECC for each step.
* So, it's the same as sunxi_get_oob_offset(),
* but it skips the next user data.
*/
static int sunxi_get_ecc_offset(struct sunxi_nand_chip *sunxi_nand,
struct nand_ecc_ctrl *ecc, int step)
{
return sunxi_get_oob_offset(sunxi_nand, ecc, step) +
sunxi_nfc_user_data_sz(sunxi_nand, step);
}
static void sunxi_nfc_hw_ecc_read_extra_oob(struct nand_chip *nand,
u8 *oob, int *cur_off,
bool randomize, int page)
{
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct mtd_info *mtd = nand_to_mtd(nand);
struct nand_ecc_ctrl *ecc = &nand->ecc;
int offset = ((ecc->bytes + 4) * ecc->steps);
int offset = sunxi_get_oob_offset(sunxi_nand, ecc, ecc->steps);
int len = mtd->oobsize - offset;
if (len <= 0)
return;
if (!cur_off || *cur_off != offset)
nand_change_read_column_op(nand, mtd->writesize, NULL, 0,
false);
if (!cur_off || *cur_off != (offset + mtd->writesize))
nand_change_read_column_op(nand, mtd->writesize + offset,
NULL, 0, false);
if (!randomize)
sunxi_nfc_read_buf(nand, oob + offset, len);
@@ -1067,6 +1172,7 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
int nchunks)
{
bool randomized = nand->options & NAND_NEED_SCRAMBLING;
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct mtd_info *mtd = nand_to_mtd(nand);
struct nand_ecc_ctrl *ecc = &nand->ecc;
@@ -1086,7 +1192,8 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
sunxi_nfc_hw_ecc_enable(nand);
sunxi_nfc_reset_user_data_len(nfc);
sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, 0);
for (i = 0; i < nchunks; i++)
sunxi_nfc_set_user_data_len(nfc, sunxi_nfc_user_data_sz(sunxi_nand, i), i);
sunxi_nfc_randomizer_config(nand, page, false);
sunxi_nfc_randomizer_enable(nand);
@@ -1121,7 +1228,8 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
for (i = 0; i < nchunks; i++) {
int data_off = i * ecc->size;
int oob_off = i * (ecc->bytes + USER_DATA_SZ);
unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, i);
int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i);
u8 *data = buf + data_off;
u8 *oob = nand->oob_poi + oob_off;
bool erased;
@@ -1139,10 +1247,10 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
/* TODO: use DMA to retrieve OOB */
nand_change_read_column_op(nand,
mtd->writesize + oob_off,
oob, ecc->bytes + USER_DATA_SZ, false);
oob, ecc->bytes + user_data_sz, false);
sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, i,
!i, page);
sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, i, !i,
page, user_data_sz);
}
if (erased)
@@ -1154,7 +1262,8 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
if (status & NFC_ECC_ERR_MSK(nfc)) {
for (i = 0; i < nchunks; i++) {
int data_off = i * ecc->size;
int oob_off = i * (ecc->bytes + USER_DATA_SZ);
unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, i);
int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i);
u8 *data = buf + data_off;
u8 *oob = nand->oob_poi + oob_off;
@@ -1174,10 +1283,10 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
/* TODO: use DMA to retrieve OOB */
nand_change_read_column_op(nand,
mtd->writesize + oob_off,
oob, ecc->bytes + USER_DATA_SZ, false);
oob, ecc->bytes + user_data_sz, false);
ret = nand_check_erased_ecc_chunk(data, ecc->size, oob,
ecc->bytes + USER_DATA_SZ,
ecc->bytes + user_data_sz,
NULL, 0,
ecc->strength);
if (ret >= 0)
@@ -1198,12 +1307,17 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
static int sunxi_nfc_hw_ecc_write_chunk(struct nand_chip *nand,
const u8 *data, int data_off,
const u8 *oob, int oob_off,
int *cur_off, bool bbm,
int *cur_off, int step,
int page)
{
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, step);
struct nand_ecc_ctrl *ecc = &nand->ecc;
bool bbm = !step;
int ret;
/* From the controller point of view, we are at step 0 */
const int nfc_step = 0;
if (data_off != *cur_off)
nand_change_write_column_op(nand, data_off, NULL, 0, false);
@@ -1219,9 +1333,8 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct nand_chip *nand,
sunxi_nfc_randomizer_config(nand, page, false);
sunxi_nfc_randomizer_enable(nand);
sunxi_nfc_reset_user_data_len(nfc);
sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, 0);
sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, 0, bbm, page);
sunxi_nfc_set_user_data_len(nfc, user_data_sz, nfc_step);
sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, nfc_step, bbm, page);
writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
NFC_ACCESS_DIR | NFC_ECC_OP,
@@ -1232,7 +1345,7 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct nand_chip *nand,
if (ret)
return ret;
*cur_off = oob_off + ecc->bytes + USER_DATA_SZ;
*cur_off = oob_off + ecc->bytes + user_data_sz;
return 0;
}
@@ -1242,8 +1355,9 @@ static void sunxi_nfc_hw_ecc_write_extra_oob(struct nand_chip *nand,
int page)
{
struct mtd_info *mtd = nand_to_mtd(nand);
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct nand_ecc_ctrl *ecc = &nand->ecc;
int offset = ((ecc->bytes + USER_DATA_SZ) * ecc->steps);
int offset = sunxi_get_oob_offset(sunxi_nand, ecc, ecc->steps);
int len = mtd->oobsize - offset;
if (len <= 0)
@@ -1262,6 +1376,8 @@ static void sunxi_nfc_hw_ecc_write_extra_oob(struct nand_chip *nand,
static int sunxi_nfc_hw_ecc_read_page(struct nand_chip *nand, uint8_t *buf,
int oob_required, int page)
{
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct mtd_info *mtd = nand_to_mtd(nand);
struct nand_ecc_ctrl *ecc = &nand->ecc;
unsigned int max_bitflips = 0;
@@ -1274,16 +1390,17 @@ static int sunxi_nfc_hw_ecc_read_page(struct nand_chip *nand, uint8_t *buf,
sunxi_nfc_hw_ecc_enable(nand);
sunxi_nfc_reset_user_data_len(nfc);
for (i = 0; i < ecc->steps; i++) {
int data_off = i * ecc->size;
int oob_off = i * (ecc->bytes + USER_DATA_SZ);
int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i);
u8 *data = buf + data_off;
u8 *oob = nand->oob_poi + oob_off;
ret = sunxi_nfc_hw_ecc_read_chunk(nand, data, data_off, oob,
oob_off + mtd->writesize,
&cur_off, &max_bitflips,
!i, oob_required, page);
i, oob_required, page);
if (ret < 0)
return ret;
else if (ret)
@@ -1321,6 +1438,8 @@ static int sunxi_nfc_hw_ecc_read_subpage(struct nand_chip *nand,
u32 data_offs, u32 readlen,
u8 *bufpoi, int page)
{
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct mtd_info *mtd = nand_to_mtd(nand);
struct nand_ecc_ctrl *ecc = &nand->ecc;
int ret, i, cur_off = 0;
@@ -1332,17 +1451,18 @@ static int sunxi_nfc_hw_ecc_read_subpage(struct nand_chip *nand,
sunxi_nfc_hw_ecc_enable(nand);
sunxi_nfc_reset_user_data_len(nfc);
for (i = data_offs / ecc->size;
i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) {
int data_off = i * ecc->size;
int oob_off = i * (ecc->bytes + USER_DATA_SZ);
int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i);
u8 *data = bufpoi + data_off;
u8 *oob = nand->oob_poi + oob_off;
ret = sunxi_nfc_hw_ecc_read_chunk(nand, data, data_off,
oob,
oob_off + mtd->writesize,
&cur_off, &max_bitflips, !i,
&cur_off, &max_bitflips, i,
false, page);
if (ret < 0)
return ret;
@@ -1377,6 +1497,8 @@ static int sunxi_nfc_hw_ecc_write_page(struct nand_chip *nand,
const uint8_t *buf, int oob_required,
int page)
{
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct mtd_info *mtd = nand_to_mtd(nand);
struct nand_ecc_ctrl *ecc = &nand->ecc;
int ret, i, cur_off = 0;
@@ -1387,15 +1509,16 @@ static int sunxi_nfc_hw_ecc_write_page(struct nand_chip *nand,
sunxi_nfc_hw_ecc_enable(nand);
sunxi_nfc_reset_user_data_len(nfc);
for (i = 0; i < ecc->steps; i++) {
int data_off = i * ecc->size;
int oob_off = i * (ecc->bytes + USER_DATA_SZ);
int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i);
const u8 *data = buf + data_off;
const u8 *oob = nand->oob_poi + oob_off;
ret = sunxi_nfc_hw_ecc_write_chunk(nand, data, data_off, oob,
oob_off + mtd->writesize,
&cur_off, !i, page);
&cur_off, i, page);
if (ret)
return ret;
}
@@ -1414,6 +1537,8 @@ static int sunxi_nfc_hw_ecc_write_subpage(struct nand_chip *nand,
const u8 *buf, int oob_required,
int page)
{
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct mtd_info *mtd = nand_to_mtd(nand);
struct nand_ecc_ctrl *ecc = &nand->ecc;
int ret, i, cur_off = 0;
@@ -1424,16 +1549,17 @@ static int sunxi_nfc_hw_ecc_write_subpage(struct nand_chip *nand,
sunxi_nfc_hw_ecc_enable(nand);
sunxi_nfc_reset_user_data_len(nfc);
for (i = data_offs / ecc->size;
i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) {
int data_off = i * ecc->size;
int oob_off = i * (ecc->bytes + USER_DATA_SZ);
int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i);
const u8 *data = buf + data_off;
const u8 *oob = nand->oob_poi + oob_off;
ret = sunxi_nfc_hw_ecc_write_chunk(nand, data, data_off, oob,
oob_off + mtd->writesize,
&cur_off, !i, page);
&cur_off, i, page);
if (ret)
return ret;
}
@@ -1449,6 +1575,7 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand,
int page)
{
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct nand_ecc_ctrl *ecc = &nand->ecc;
struct scatterlist sg;
u32 wait;
@@ -1467,10 +1594,12 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand,
sunxi_nfc_reset_user_data_len(nfc);
for (i = 0; i < ecc->steps; i++) {
const u8 *oob = nand->oob_poi + (i * (ecc->bytes + USER_DATA_SZ));
unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, i);
int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i);
const u8 *oob = nand->oob_poi + oob_off;
sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, i, !i, page);
sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, i);
sunxi_nfc_set_user_data_len(nfc, user_data_sz, i);
}
nand_prog_page_begin_op(nand, page, 0, NULL, 0);
@@ -1734,11 +1863,12 @@ static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct nand_ecc_ctrl *ecc = &nand->ecc;
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
if (section >= ecc->steps)
return -ERANGE;
oobregion->offset = section * (ecc->bytes + USER_DATA_SZ) + 4;
oobregion->offset = sunxi_get_ecc_offset(sunxi_nand, ecc, section);
oobregion->length = ecc->bytes;
return 0;
@@ -1749,35 +1879,30 @@ static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section,
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct nand_ecc_ctrl *ecc = &nand->ecc;
if (section > ecc->steps)
return -ERANGE;
/*
* The first 2 bytes are used for BB markers, hence we
* only have 2 bytes available in the first user data
* section.
*/
if (!section && ecc->engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) {
oobregion->offset = 2;
oobregion->length = 2;
return 0;
}
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, section);
/*
* The controller does not provide access to OOB bytes
* past the end of the ECC data.
*/
if (section == ecc->steps && ecc->engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST)
if (section >= ecc->steps)
return -ERANGE;
oobregion->offset = section * (ecc->bytes + USER_DATA_SZ);
/*
* The first 2 bytes are used for BB markers, hence we
* only have user_data_sz - 2 bytes available in the first user data
* section.
*/
if (section == 0) {
oobregion->offset = 2;
oobregion->length = user_data_sz - 2;
if (section < ecc->steps)
oobregion->length = USER_DATA_SZ;
else
oobregion->length = mtd->oobsize - oobregion->offset;
return 0;
}
oobregion->offset = sunxi_get_ecc_offset(sunxi_nand, ecc, section);
oobregion->length = user_data_sz;
return 0;
}
@@ -1787,6 +1912,43 @@ static const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = {
.free = sunxi_nand_ooblayout_free,
};
static void sunxi_nand_detach_chip(struct nand_chip *nand)
{
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
devm_kfree(nfc->dev, sunxi_nand->user_data_bytes);
sunxi_nand->user_data_bytes = NULL;
}
static int sunxi_nfc_maximize_user_data(struct nand_chip *nand, uint32_t oobsize,
int ecc_bytes, int nsectors)
{
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
const struct sunxi_nfc_caps *c = nfc->caps;
int remaining_bytes = oobsize - (ecc_bytes * nsectors);
int i, step;
sunxi_nand->user_data_bytes = devm_kzalloc(nfc->dev, nsectors,
GFP_KERNEL);
if (!sunxi_nand->user_data_bytes)
return -ENOMEM;
for (step = 0; (step < nsectors) && (remaining_bytes > 0); step++) {
for (i = 0; i < c->nuser_data_tab; i++) {
if (c->user_data_len_tab[i] > remaining_bytes)
break;
sunxi_nand->user_data_bytes[step] = c->user_data_len_tab[i];
}
remaining_bytes -= sunxi_nand->user_data_bytes[step];
if (sunxi_nand->user_data_bytes[step] == 0)
break;
}
return 0;
}
static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
struct nand_ecc_ctrl *ecc,
struct device_node *np)
@@ -1796,20 +1958,50 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
const u8 *strengths = nfc->caps->ecc_strengths;
struct mtd_info *mtd = nand_to_mtd(nand);
struct nand_device *nanddev = mtd_to_nanddev(mtd);
int total_user_data_sz = 0;
int nsectors;
int ecc_mode;
int i;
if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) {
int bytes;
int bytes = mtd->oobsize;
ecc->size = 1024;
nsectors = mtd->writesize / ecc->size;
/* Reserve 2 bytes for the BBM */
bytes = (mtd->oobsize - 2) / nsectors;
if (!nfc->caps->reg_user_data_len) {
/*
* If there's a fixed user data length, subtract it before
* computing the max ECC strength
*/
/* 4 non-ECC bytes are added before each ECC bytes section */
bytes -= USER_DATA_SZ;
for (i = 0; i < nsectors; i++)
total_user_data_sz += sunxi_nfc_user_data_sz(sunxi_nand, i);
/*
* The 2 BBM bytes should not be removed from the grand total,
* because they are part of the USER_DATA_SZ.
* But we can't modify that for older platform since it may
* result in a stronger ECC at the end, and break the
* compatibility.
*/
if (nfc->caps->legacy_max_strength)
bytes -= 2;
bytes -= total_user_data_sz;
} else {
/*
* remove at least the BBM size before computing the
* max ECC
*/
bytes -= 2;
}
/*
* Once all user data has been subtracted, the rest can be used
* for ECC bytes
*/
bytes /= nsectors;
/* and bytes has to be even. */
if (bytes % 2)
@@ -1838,18 +2030,18 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
}
/* Add ECC info retrieval from DT */
for (i = 0; i < nfc->caps->nstrengths; i++) {
if (ecc->strength <= strengths[i]) {
for (ecc_mode = 0; ecc_mode < nfc->caps->nstrengths; ecc_mode++) {
if (ecc->strength <= strengths[ecc_mode]) {
/*
* Update ecc->strength value with the actual strength
* that will be used by the ECC engine.
*/
ecc->strength = strengths[i];
ecc->strength = strengths[ecc_mode];
break;
}
}
if (i >= nfc->caps->nstrengths) {
if (ecc_mode >= nfc->caps->nstrengths) {
dev_err(nfc->dev, "unsupported strength\n");
return -ENOTSUPP;
}
@@ -1862,7 +2054,19 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
nsectors = mtd->writesize / ecc->size;
if (mtd->oobsize < ((ecc->bytes + USER_DATA_SZ) * nsectors))
/*
* The rationale for variable data length is to prioritize maximum ECC
* strength, and then use the remaining space for user data.
*/
if (nfc->caps->reg_user_data_len)
sunxi_nfc_maximize_user_data(nand, mtd->oobsize, ecc->bytes,
nsectors);
if (total_user_data_sz == 0)
for (i = 0; i < nsectors; i++)
total_user_data_sz += sunxi_nfc_user_data_sz(sunxi_nand, i);
if (mtd->oobsize < (ecc->bytes * nsectors + total_user_data_sz))
return -EINVAL;
ecc->read_oob = sunxi_nfc_hw_ecc_read_oob;
@@ -1885,7 +2089,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
ecc->read_oob_raw = nand_read_oob_std;
ecc->write_oob_raw = nand_write_oob_std;
sunxi_nand->ecc.ecc_ctl = NFC_ECC_MODE(nfc, i) | NFC_ECC_EXCEPTION |
sunxi_nand->ecc.ecc_ctl = NFC_ECC_MODE(nfc, ecc_mode) | NFC_ECC_EXCEPTION |
NFC_ECC_PIPELINE | NFC_ECC_EN;
if (ecc->size == 512) {
@@ -2092,6 +2296,7 @@ static int sunxi_nfc_exec_op(struct nand_chip *nand,
static const struct nand_controller_ops sunxi_nand_controller_ops = {
.attach_chip = sunxi_nand_attach_chip,
.detach_chip = sunxi_nand_detach_chip,
.setup_interface = sunxi_nfc_setup_interface,
.exec_op = sunxi_nfc_exec_op,
};
@@ -2373,6 +2578,7 @@ static const u8 sunxi_user_data_len_h6[] = {
static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = {
.has_ecc_block_512 = true,
.legacy_max_strength = true,
.reg_io_data = NFC_REG_A10_IO_DATA,
.reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT,
.reg_user_data = NFC_REG_A10_USER_DATA,
@@ -2394,6 +2600,7 @@ static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = {
static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = {
.has_mdma = true,
.has_ecc_block_512 = true,
.legacy_max_strength = true,
.reg_io_data = NFC_REG_A23_IO_DATA,
.reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT,
.reg_user_data = NFC_REG_A10_USER_DATA,

View File

@@ -337,16 +337,19 @@ static int w25n0xjw_hs_cfg(struct spinand_device *spinand,
if (iface != SSDR)
return -EOPNOTSUPP;
/*
* SDR dual and quad I/O operations over 104MHz require the HS bit to
* enable a few more dummy cycles.
*/
op = spinand->op_templates->read_cache;
if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr)
hs = false;
else if (op->cmd.buswidth == 1 && op->addr.buswidth == 1 &&
op->dummy.buswidth == 1 && op->data.buswidth == 1)
else if (op->cmd.buswidth != 1 || op->addr.buswidth == 1)
hs = false;
else if (op->max_freq && op->max_freq <= 104 * HZ_PER_MHZ)
hs = false;
else if (!op->max_freq)
hs = true;
else
hs = false;
hs = true;
ret = spinand_read_reg_op(spinand, W25N0XJW_SR4, &sr4);
if (ret)
@@ -485,7 +488,7 @@ static const struct spinand_info winbond_spinand_table[] = {
SPINAND_INFO_OP_VARIANTS(&read_cache_dual_quad_dtr_variants,
&write_cache_variants,
&update_cache_variants),
0,
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&w25n01jw_ooblayout, NULL),
SPINAND_CONFIGURE_CHIP(w25n0xjw_hs_cfg)),
SPINAND_INFO("W25N01KV", /* 3.3V */
@@ -549,7 +552,7 @@ static const struct spinand_info winbond_spinand_table[] = {
SPINAND_INFO_OP_VARIANTS(&read_cache_dual_quad_dtr_variants,
&write_cache_variants,
&update_cache_variants),
0,
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
SPINAND_CONFIGURE_CHIP(w25n0xjw_hs_cfg)),
SPINAND_INFO("W25N02KV", /* 3.3V */

View File

@@ -477,8 +477,9 @@ struct spinand_ecc_info {
const struct mtd_ooblayout_ops *ooblayout;
};
#define SPINAND_HAS_QE_BIT BIT(0)
#define SPINAND_HAS_CR_FEAT_BIT BIT(1)
/* SPI NAND flags */
#define SPINAND_HAS_QE_BIT BIT(0)
#define SPINAND_HAS_CR_FEAT_BIT BIT(1)
#define SPINAND_HAS_PROG_PLANE_SELECT_BIT BIT(2)
#define SPINAND_HAS_READ_PLANE_SELECT_BIT BIT(3)
#define SPINAND_NO_RAW_ACCESS BIT(4)