mirror of
https://github.com/openwrt/openwrt.git
synced 2026-04-23 15:48:44 +04:00
realtek: backport ECC driver
Upstream will get support for the Realtek ECC engine with 6.18. To make use of this in Openwrt - backport upstream patches - change config so that ECC will be built for nand subtargets - define ECC engine in RTL93xx DTS. Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de> Link: https://github.com/openwrt/openwrt/pull/19746 Signed-off-by: Robert Marko <robimarko@gmail.com>
This commit is contained in:
committed by
Robert Marko
parent
36d8d19993
commit
b49f9d9804
@@ -0,0 +1,329 @@
|
||||
From 6b88293aae7fb78872e5cc1ec36e2f750ae12e38 Mon Sep 17 00:00:00 2001
|
||||
From: Markus Stockhausen <markus.stockhausen@gmx.de>
|
||||
Date: Wed, 10 Sep 2025 14:32:58 -0400
|
||||
Subject: mtd: nand: move nand_check_erased_ecc_chunk() to nand/core
|
||||
|
||||
The check function for bitflips in erased blocks will be needed
|
||||
by the Realtek ECC engine driver (which is currently under
|
||||
development). Right now it is located in raw/nand_base.c.
|
||||
While this is sufficient for the current usecases, there is
|
||||
no real dependency for an ECC engine on the raw nand library.
|
||||
|
||||
Move the function over to a more generic place in core library.
|
||||
|
||||
Suggested-by: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
|
||||
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
---
|
||||
drivers/mtd/nand/core.c | 131 +++++++++++++++++++++++++++++++++++++++
|
||||
drivers/mtd/nand/raw/nand_base.c | 131 ---------------------------------------
|
||||
include/linux/mtd/nand.h | 5 ++
|
||||
include/linux/mtd/rawnand.h | 5 --
|
||||
4 files changed, 136 insertions(+), 136 deletions(-)
|
||||
|
||||
--- a/drivers/mtd/nand/core.c
|
||||
+++ b/drivers/mtd/nand/core.c
|
||||
@@ -13,6 +13,137 @@
|
||||
#include <linux/mtd/nand.h>
|
||||
|
||||
/**
|
||||
+ * nand_check_erased_buf - check if a buffer contains (almost) only 0xff data
|
||||
+ * @buf: buffer to test
|
||||
+ * @len: buffer length
|
||||
+ * @bitflips_threshold: maximum number of bitflips
|
||||
+ *
|
||||
+ * Check if a buffer contains only 0xff, which means the underlying region
|
||||
+ * has been erased and is ready to be programmed.
|
||||
+ * The bitflips_threshold specify the maximum number of bitflips before
|
||||
+ * considering the region is not erased.
|
||||
+ * Note: The logic of this function has been extracted from the memweight
|
||||
+ * implementation, except that nand_check_erased_buf function exit before
|
||||
+ * testing the whole buffer if the number of bitflips exceed the
|
||||
+ * bitflips_threshold value.
|
||||
+ *
|
||||
+ * Returns a positive number of bitflips less than or equal to
|
||||
+ * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the
|
||||
+ * threshold.
|
||||
+ */
|
||||
+static int nand_check_erased_buf(void *buf, int len, int bitflips_threshold)
|
||||
+{
|
||||
+ const unsigned char *bitmap = buf;
|
||||
+ int bitflips = 0;
|
||||
+ int weight;
|
||||
+
|
||||
+ for (; len && ((uintptr_t)bitmap) % sizeof(long);
|
||||
+ len--, bitmap++) {
|
||||
+ weight = hweight8(*bitmap);
|
||||
+ bitflips += BITS_PER_BYTE - weight;
|
||||
+ if (unlikely(bitflips > bitflips_threshold))
|
||||
+ return -EBADMSG;
|
||||
+ }
|
||||
+
|
||||
+ for (; len >= sizeof(long);
|
||||
+ len -= sizeof(long), bitmap += sizeof(long)) {
|
||||
+ unsigned long d = *((unsigned long *)bitmap);
|
||||
+ if (d == ~0UL)
|
||||
+ continue;
|
||||
+ weight = hweight_long(d);
|
||||
+ bitflips += BITS_PER_LONG - weight;
|
||||
+ if (unlikely(bitflips > bitflips_threshold))
|
||||
+ return -EBADMSG;
|
||||
+ }
|
||||
+
|
||||
+ for (; len > 0; len--, bitmap++) {
|
||||
+ weight = hweight8(*bitmap);
|
||||
+ bitflips += BITS_PER_BYTE - weight;
|
||||
+ if (unlikely(bitflips > bitflips_threshold))
|
||||
+ return -EBADMSG;
|
||||
+ }
|
||||
+
|
||||
+ return bitflips;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * nand_check_erased_ecc_chunk - check if an ECC chunk contains (almost) only
|
||||
+ * 0xff data
|
||||
+ * @data: data buffer to test
|
||||
+ * @datalen: data length
|
||||
+ * @ecc: ECC buffer
|
||||
+ * @ecclen: ECC length
|
||||
+ * @extraoob: extra OOB buffer
|
||||
+ * @extraooblen: extra OOB length
|
||||
+ * @bitflips_threshold: maximum number of bitflips
|
||||
+ *
|
||||
+ * Check if a data buffer and its associated ECC and OOB data contains only
|
||||
+ * 0xff pattern, which means the underlying region has been erased and is
|
||||
+ * ready to be programmed.
|
||||
+ * The bitflips_threshold specify the maximum number of bitflips before
|
||||
+ * considering the region as not erased.
|
||||
+ *
|
||||
+ * Note:
|
||||
+ * 1/ ECC algorithms are working on pre-defined block sizes which are usually
|
||||
+ * different from the NAND page size. When fixing bitflips, ECC engines will
|
||||
+ * report the number of errors per chunk, and the NAND core infrastructure
|
||||
+ * expect you to return the maximum number of bitflips for the whole page.
|
||||
+ * This is why you should always use this function on a single chunk and
|
||||
+ * not on the whole page. After checking each chunk you should update your
|
||||
+ * max_bitflips value accordingly.
|
||||
+ * 2/ When checking for bitflips in erased pages you should not only check
|
||||
+ * the payload data but also their associated ECC data, because a user might
|
||||
+ * have programmed almost all bits to 1 but a few. In this case, we
|
||||
+ * shouldn't consider the chunk as erased, and checking ECC bytes prevent
|
||||
+ * this case.
|
||||
+ * 3/ The extraoob argument is optional, and should be used if some of your OOB
|
||||
+ * data are protected by the ECC engine.
|
||||
+ * It could also be used if you support subpages and want to attach some
|
||||
+ * extra OOB data to an ECC chunk.
|
||||
+ *
|
||||
+ * Returns a positive number of bitflips less than or equal to
|
||||
+ * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the
|
||||
+ * threshold. In case of success, the passed buffers are filled with 0xff.
|
||||
+ */
|
||||
+int nand_check_erased_ecc_chunk(void *data, int datalen,
|
||||
+ void *ecc, int ecclen,
|
||||
+ void *extraoob, int extraooblen,
|
||||
+ int bitflips_threshold)
|
||||
+{
|
||||
+ int data_bitflips = 0, ecc_bitflips = 0, extraoob_bitflips = 0;
|
||||
+
|
||||
+ data_bitflips = nand_check_erased_buf(data, datalen,
|
||||
+ bitflips_threshold);
|
||||
+ if (data_bitflips < 0)
|
||||
+ return data_bitflips;
|
||||
+
|
||||
+ bitflips_threshold -= data_bitflips;
|
||||
+
|
||||
+ ecc_bitflips = nand_check_erased_buf(ecc, ecclen, bitflips_threshold);
|
||||
+ if (ecc_bitflips < 0)
|
||||
+ return ecc_bitflips;
|
||||
+
|
||||
+ bitflips_threshold -= ecc_bitflips;
|
||||
+
|
||||
+ extraoob_bitflips = nand_check_erased_buf(extraoob, extraooblen,
|
||||
+ bitflips_threshold);
|
||||
+ if (extraoob_bitflips < 0)
|
||||
+ return extraoob_bitflips;
|
||||
+
|
||||
+ if (data_bitflips)
|
||||
+ memset(data, 0xff, datalen);
|
||||
+
|
||||
+ if (ecc_bitflips)
|
||||
+ memset(ecc, 0xff, ecclen);
|
||||
+
|
||||
+ if (extraoob_bitflips)
|
||||
+ memset(extraoob, 0xff, extraooblen);
|
||||
+
|
||||
+ return data_bitflips + ecc_bitflips + extraoob_bitflips;
|
||||
+}
|
||||
+EXPORT_SYMBOL(nand_check_erased_ecc_chunk);
|
||||
+
|
||||
+/**
|
||||
* nanddev_isbad() - Check if a block is bad
|
||||
* @nand: NAND device
|
||||
* @pos: position pointing to the block we want to check
|
||||
--- a/drivers/mtd/nand/raw/nand_base.c
|
||||
+++ b/drivers/mtd/nand/raw/nand_base.c
|
||||
@@ -2784,137 +2784,6 @@ int nand_set_features(struct nand_chip *
|
||||
}
|
||||
|
||||
/**
|
||||
- * nand_check_erased_buf - check if a buffer contains (almost) only 0xff data
|
||||
- * @buf: buffer to test
|
||||
- * @len: buffer length
|
||||
- * @bitflips_threshold: maximum number of bitflips
|
||||
- *
|
||||
- * Check if a buffer contains only 0xff, which means the underlying region
|
||||
- * has been erased and is ready to be programmed.
|
||||
- * The bitflips_threshold specify the maximum number of bitflips before
|
||||
- * considering the region is not erased.
|
||||
- * Note: The logic of this function has been extracted from the memweight
|
||||
- * implementation, except that nand_check_erased_buf function exit before
|
||||
- * testing the whole buffer if the number of bitflips exceed the
|
||||
- * bitflips_threshold value.
|
||||
- *
|
||||
- * Returns a positive number of bitflips less than or equal to
|
||||
- * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the
|
||||
- * threshold.
|
||||
- */
|
||||
-static int nand_check_erased_buf(void *buf, int len, int bitflips_threshold)
|
||||
-{
|
||||
- const unsigned char *bitmap = buf;
|
||||
- int bitflips = 0;
|
||||
- int weight;
|
||||
-
|
||||
- for (; len && ((uintptr_t)bitmap) % sizeof(long);
|
||||
- len--, bitmap++) {
|
||||
- weight = hweight8(*bitmap);
|
||||
- bitflips += BITS_PER_BYTE - weight;
|
||||
- if (unlikely(bitflips > bitflips_threshold))
|
||||
- return -EBADMSG;
|
||||
- }
|
||||
-
|
||||
- for (; len >= sizeof(long);
|
||||
- len -= sizeof(long), bitmap += sizeof(long)) {
|
||||
- unsigned long d = *((unsigned long *)bitmap);
|
||||
- if (d == ~0UL)
|
||||
- continue;
|
||||
- weight = hweight_long(d);
|
||||
- bitflips += BITS_PER_LONG - weight;
|
||||
- if (unlikely(bitflips > bitflips_threshold))
|
||||
- return -EBADMSG;
|
||||
- }
|
||||
-
|
||||
- for (; len > 0; len--, bitmap++) {
|
||||
- weight = hweight8(*bitmap);
|
||||
- bitflips += BITS_PER_BYTE - weight;
|
||||
- if (unlikely(bitflips > bitflips_threshold))
|
||||
- return -EBADMSG;
|
||||
- }
|
||||
-
|
||||
- return bitflips;
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
- * nand_check_erased_ecc_chunk - check if an ECC chunk contains (almost) only
|
||||
- * 0xff data
|
||||
- * @data: data buffer to test
|
||||
- * @datalen: data length
|
||||
- * @ecc: ECC buffer
|
||||
- * @ecclen: ECC length
|
||||
- * @extraoob: extra OOB buffer
|
||||
- * @extraooblen: extra OOB length
|
||||
- * @bitflips_threshold: maximum number of bitflips
|
||||
- *
|
||||
- * Check if a data buffer and its associated ECC and OOB data contains only
|
||||
- * 0xff pattern, which means the underlying region has been erased and is
|
||||
- * ready to be programmed.
|
||||
- * The bitflips_threshold specify the maximum number of bitflips before
|
||||
- * considering the region as not erased.
|
||||
- *
|
||||
- * Note:
|
||||
- * 1/ ECC algorithms are working on pre-defined block sizes which are usually
|
||||
- * different from the NAND page size. When fixing bitflips, ECC engines will
|
||||
- * report the number of errors per chunk, and the NAND core infrastructure
|
||||
- * expect you to return the maximum number of bitflips for the whole page.
|
||||
- * This is why you should always use this function on a single chunk and
|
||||
- * not on the whole page. After checking each chunk you should update your
|
||||
- * max_bitflips value accordingly.
|
||||
- * 2/ When checking for bitflips in erased pages you should not only check
|
||||
- * the payload data but also their associated ECC data, because a user might
|
||||
- * have programmed almost all bits to 1 but a few. In this case, we
|
||||
- * shouldn't consider the chunk as erased, and checking ECC bytes prevent
|
||||
- * this case.
|
||||
- * 3/ The extraoob argument is optional, and should be used if some of your OOB
|
||||
- * data are protected by the ECC engine.
|
||||
- * It could also be used if you support subpages and want to attach some
|
||||
- * extra OOB data to an ECC chunk.
|
||||
- *
|
||||
- * Returns a positive number of bitflips less than or equal to
|
||||
- * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the
|
||||
- * threshold. In case of success, the passed buffers are filled with 0xff.
|
||||
- */
|
||||
-int nand_check_erased_ecc_chunk(void *data, int datalen,
|
||||
- void *ecc, int ecclen,
|
||||
- void *extraoob, int extraooblen,
|
||||
- int bitflips_threshold)
|
||||
-{
|
||||
- int data_bitflips = 0, ecc_bitflips = 0, extraoob_bitflips = 0;
|
||||
-
|
||||
- data_bitflips = nand_check_erased_buf(data, datalen,
|
||||
- bitflips_threshold);
|
||||
- if (data_bitflips < 0)
|
||||
- return data_bitflips;
|
||||
-
|
||||
- bitflips_threshold -= data_bitflips;
|
||||
-
|
||||
- ecc_bitflips = nand_check_erased_buf(ecc, ecclen, bitflips_threshold);
|
||||
- if (ecc_bitflips < 0)
|
||||
- return ecc_bitflips;
|
||||
-
|
||||
- bitflips_threshold -= ecc_bitflips;
|
||||
-
|
||||
- extraoob_bitflips = nand_check_erased_buf(extraoob, extraooblen,
|
||||
- bitflips_threshold);
|
||||
- if (extraoob_bitflips < 0)
|
||||
- return extraoob_bitflips;
|
||||
-
|
||||
- if (data_bitflips)
|
||||
- memset(data, 0xff, datalen);
|
||||
-
|
||||
- if (ecc_bitflips)
|
||||
- memset(ecc, 0xff, ecclen);
|
||||
-
|
||||
- if (extraoob_bitflips)
|
||||
- memset(extraoob, 0xff, extraooblen);
|
||||
-
|
||||
- return data_bitflips + ecc_bitflips + extraoob_bitflips;
|
||||
-}
|
||||
-EXPORT_SYMBOL(nand_check_erased_ecc_chunk);
|
||||
-
|
||||
-/**
|
||||
* nand_read_page_raw_notsupp - dummy read raw page function
|
||||
* @chip: nand chip info structure
|
||||
* @buf: buffer to store read data
|
||||
--- a/include/linux/mtd/nand.h
|
||||
+++ b/include/linux/mtd/nand.h
|
||||
@@ -1136,4 +1136,9 @@ static inline bool nanddev_bbt_is_initia
|
||||
int nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo);
|
||||
int nanddev_mtd_max_bad_blocks(struct mtd_info *mtd, loff_t offs, size_t len);
|
||||
|
||||
+int nand_check_erased_ecc_chunk(void *data, int datalen,
|
||||
+ void *ecc, int ecclen,
|
||||
+ void *extraoob, int extraooblen,
|
||||
+ int threshold);
|
||||
+
|
||||
#endif /* __LINUX_MTD_NAND_H */
|
||||
--- a/include/linux/mtd/rawnand.h
|
||||
+++ b/include/linux/mtd/rawnand.h
|
||||
@@ -1519,11 +1519,6 @@ int rawnand_sw_bch_correct(struct nand_c
|
||||
unsigned char *read_ecc, unsigned char *calc_ecc);
|
||||
void rawnand_sw_bch_cleanup(struct nand_chip *chip);
|
||||
|
||||
-int nand_check_erased_ecc_chunk(void *data, int datalen,
|
||||
- void *ecc, int ecclen,
|
||||
- void *extraoob, int extraooblen,
|
||||
- int threshold);
|
||||
-
|
||||
int nand_ecc_choose_conf(struct nand_chip *chip,
|
||||
const struct nand_ecc_caps *caps, int oobavail);
|
||||
|
||||
@@ -55,6 +55,13 @@
|
||||
#size-cells = <1>;
|
||||
ranges = <0x0 0x18000000 0x20000>;
|
||||
|
||||
ecc0: ecc@1a600 {
|
||||
compatible = "realtek,rtl9301-ecc";
|
||||
reg = <0x1a600 0x54>;
|
||||
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
intc: interrupt-controller@3000 {
|
||||
compatible = "realtek,rtl9300-intc", "realtek,rtl-intc";
|
||||
reg = <0x3000 0x18>, <0x3018 0x18>;
|
||||
|
||||
@@ -90,6 +90,13 @@
|
||||
#size-cells = <1>;
|
||||
ranges = <0x0 0x18000000 0x20000>;
|
||||
|
||||
ecc0: ecc@1a600 {
|
||||
compatible = "realtek,rtl9301-ecc";
|
||||
reg = <0x1a600 0x54>;
|
||||
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
spi0: spi@1200 {
|
||||
status = "okay";
|
||||
|
||||
|
||||
@@ -0,0 +1,521 @@
|
||||
From 3148d0e5b1c5733d69ec51b70c8280e46488750a Mon Sep 17 00:00:00 2001
|
||||
From: Markus Stockhausen <markus.stockhausen@gmx.de>
|
||||
Date: Fri, 19 Sep 2025 03:52:01 -0400
|
||||
Subject: mtd: nand: realtek-ecc: Add Realtek external ECC engine support
|
||||
|
||||
The Realtek RTl93xx switch SoC series has a built in ECC controller
|
||||
that can provide BCH6 or BCH12 over 512 data and 6 tag bytes. It
|
||||
generates 10 (BCH6) or 20 (BCH12) bytes of parity.
|
||||
|
||||
This engine will most likely work in conjunction with the Realtek
|
||||
spi-mem based NAND controller but can work on its own. Therefore
|
||||
the initial implementation will be of type external.
|
||||
|
||||
Remark! The engine can support any data blocks that are multiples
|
||||
of 512 bytes. For now limit it to data+oob layouts that have been
|
||||
analyzed from existing devices. This way it keeps compatibility
|
||||
and pre-existing vendor data can be read.
|
||||
|
||||
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
|
||||
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
---
|
||||
drivers/mtd/nand/Kconfig | 8 +
|
||||
drivers/mtd/nand/Makefile | 1 +
|
||||
drivers/mtd/nand/ecc-realtek.c | 464 +++++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 473 insertions(+)
|
||||
create mode 100644 drivers/mtd/nand/ecc-realtek.c
|
||||
|
||||
--- a/drivers/mtd/nand/Kconfig
|
||||
+++ b/drivers/mtd/nand/Kconfig
|
||||
@@ -65,6 +65,14 @@ config MTD_NAND_ECC_MEDIATEK
|
||||
help
|
||||
This enables support for the hardware ECC engine from Mediatek.
|
||||
|
||||
+config MTD_NAND_ECC_REALTEK
|
||||
+ tristate "Realtek RTL93xx hardware ECC engine"
|
||||
+ depends on HAS_IOMEM
|
||||
+ depends on MACH_REALTEK_RTL || COMPILE_TEST
|
||||
+ select MTD_NAND_ECC
|
||||
+ help
|
||||
+ This enables support for the hardware ECC engine from Realtek.
|
||||
+
|
||||
endmenu
|
||||
|
||||
endmenu
|
||||
--- a/drivers/mtd/nand/Makefile
|
||||
+++ b/drivers/mtd/nand/Makefile
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
nandcore-objs := core.o bbt.o
|
||||
obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o
|
||||
+obj-$(CONFIG_MTD_NAND_ECC_REALTEK) += ecc-realtek.o
|
||||
obj-$(CONFIG_MTD_NAND_ECC_MEDIATEK) += ecc-mtk.o
|
||||
obj-$(CONFIG_MTD_NAND_MTK_BMT) += mtk_bmt.o mtk_bmt_v2.o mtk_bmt_bbt.o mtk_bmt_nmbm.o
|
||||
ifeq ($(CONFIG_SPI_QPIC_SNAND),y)
|
||||
--- /dev/null
|
||||
+++ b/drivers/mtd/nand/ecc-realtek.c
|
||||
@@ -0,0 +1,464 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0
|
||||
+/*
|
||||
+ * Support for Realtek hardware ECC engine in RTL93xx SoCs
|
||||
+ */
|
||||
+
|
||||
+#include <linux/bitfield.h>
|
||||
+#include <linux/dma-mapping.h>
|
||||
+#include <linux/mtd/nand.h>
|
||||
+#include <linux/mutex.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/regmap.h>
|
||||
+
|
||||
+/*
|
||||
+ * The Realtek ECC engine has two operation modes.
|
||||
+ *
|
||||
+ * - BCH6 : Generate 10 ECC bytes from 512 data bytes plus 6 free bytes
|
||||
+ * - 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.
|
||||
+ *
|
||||
+ * 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.
|
||||
+ *
|
||||
+ * - BBI : Bad block indicator. The first two bytes of OOB. Protected by ECC!
|
||||
+ * - tag : 6 User/free bytes. First tag "contains" 2 bytes BBI. Protected by ECC!
|
||||
+ * - syndrome : ECC/parity bytes
|
||||
+ *
|
||||
+ * Altogether this gives currently the following block layout.
|
||||
+ *
|
||||
+ * +------+------+------+------+-----+------+------+------+------+-----+-----+-----+-----+
|
||||
+ * | 512 | 512 | 512 | 512 | 2 | 4 | 6 | 6 | 6 | 10 | 10 | 10 | 10 |
|
||||
+ * +------+------+------+------+-----+------+------+------+------+-----+-----+-----+-----+
|
||||
+ * | data | data | data | data | BBI | free | free | free | free | ECC | ECC | ECC | ECC |
|
||||
+ * +------+------+------+------+-----+------+------+------+------+-----+-----+-----+-----+
|
||||
+ */
|
||||
+
|
||||
+#define RTL_ECC_ALLOWED_PAGE_SIZE 2048
|
||||
+#define RTL_ECC_ALLOWED_OOB_SIZE 64
|
||||
+#define RTL_ECC_ALLOWED_STRENGTH 6
|
||||
+
|
||||
+#define RTL_ECC_BLOCK_SIZE 512
|
||||
+#define RTL_ECC_FREE_SIZE 6
|
||||
+#define RTL_ECC_PARITY_SIZE_BCH6 10
|
||||
+#define RTL_ECC_PARITY_SIZE_BCH12 20
|
||||
+
|
||||
+/*
|
||||
+ * The engine is fed with two DMA regions. One for data (always 512 bytes) and one for free bytes
|
||||
+ * and parity (either 16 bytes for BCH6 or 26 bytes for BCH12). Start and length of each must be
|
||||
+ * aligned to a multiple of 4.
|
||||
+ */
|
||||
+
|
||||
+#define RTL_ECC_DMA_FREE_PARITY_SIZE ALIGN(RTL_ECC_FREE_SIZE + RTL_ECC_PARITY_SIZE_BCH12, 4)
|
||||
+#define RTL_ECC_DMA_SIZE (RTL_ECC_BLOCK_SIZE + RTL_ECC_DMA_FREE_PARITY_SIZE)
|
||||
+
|
||||
+#define RTL_ECC_CFG 0x00
|
||||
+#define RTL_ECC_BCH6 0
|
||||
+#define RTL_ECC_BCH12 BIT(28)
|
||||
+#define RTL_ECC_DMA_PRECISE BIT(12)
|
||||
+#define RTL_ECC_BURST_128 GENMASK(1, 0)
|
||||
+#define RTL_ECC_DMA_TRIGGER 0x08
|
||||
+#define RTL_ECC_OP_DECODE 0
|
||||
+#define RTL_ECC_OP_ENCODE BIT(0)
|
||||
+#define RTL_ECC_DMA_START 0x0c
|
||||
+#define RTL_ECC_DMA_TAG 0x10
|
||||
+#define RTL_ECC_STATUS 0x14
|
||||
+#define RTL_ECC_CORR_COUNT GENMASK(19, 12)
|
||||
+#define RTL_ECC_RESULT BIT(8)
|
||||
+#define RTL_ECC_ALL_ONE BIT(4)
|
||||
+#define RTL_ECC_OP_STATUS BIT(0)
|
||||
+
|
||||
+struct rtl_ecc_engine {
|
||||
+ struct device *dev;
|
||||
+ struct nand_ecc_engine engine;
|
||||
+ struct mutex lock;
|
||||
+ char *buf;
|
||||
+ dma_addr_t buf_dma;
|
||||
+ struct regmap *regmap;
|
||||
+};
|
||||
+
|
||||
+struct rtl_ecc_ctx {
|
||||
+ struct rtl_ecc_engine * rtlc;
|
||||
+ struct nand_ecc_req_tweak_ctx req_ctx;
|
||||
+ int steps;
|
||||
+ int bch_mode;
|
||||
+ int strength;
|
||||
+ int parity_size;
|
||||
+};
|
||||
+
|
||||
+static const struct regmap_config rtl_ecc_regmap_config = {
|
||||
+ .reg_bits = 32,
|
||||
+ .val_bits = 32,
|
||||
+ .reg_stride = 4,
|
||||
+};
|
||||
+
|
||||
+static inline void *nand_to_ctx(struct nand_device *nand)
|
||||
+{
|
||||
+ return nand->ecc.ctx.priv;
|
||||
+}
|
||||
+
|
||||
+static inline struct rtl_ecc_engine *nand_to_rtlc(struct nand_device *nand)
|
||||
+{
|
||||
+ struct nand_ecc_engine *eng = nand->ecc.engine;
|
||||
+
|
||||
+ return container_of(eng, struct rtl_ecc_engine, engine);
|
||||
+}
|
||||
+
|
||||
+static int rtl_ecc_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
+ struct mtd_oob_region *oobregion)
|
||||
+{
|
||||
+ struct nand_device *nand = mtd_to_nanddev(mtd);
|
||||
+ struct rtl_ecc_ctx *ctx = nand_to_ctx(nand);
|
||||
+
|
||||
+ if (section < 0 || section >= ctx->steps)
|
||||
+ return -ERANGE;
|
||||
+
|
||||
+ oobregion->offset = ctx->steps * RTL_ECC_FREE_SIZE + section * ctx->parity_size;
|
||||
+ oobregion->length = ctx->parity_size;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rtl_ecc_ooblayout_free(struct mtd_info *mtd, int section,
|
||||
+ struct mtd_oob_region *oobregion)
|
||||
+{
|
||||
+ struct nand_device *nand = mtd_to_nanddev(mtd);
|
||||
+ struct rtl_ecc_ctx *ctx = nand_to_ctx(nand);
|
||||
+ int bbm;
|
||||
+
|
||||
+ if (section < 0 || section >= ctx->steps)
|
||||
+ return -ERANGE;
|
||||
+
|
||||
+ /* reserve 2 BBM bytes in first block */
|
||||
+ bbm = section ? 0 : 2;
|
||||
+ oobregion->offset = section * RTL_ECC_FREE_SIZE + bbm;
|
||||
+ oobregion->length = RTL_ECC_FREE_SIZE - bbm;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct mtd_ooblayout_ops rtl_ecc_ooblayout_ops = {
|
||||
+ .ecc = rtl_ecc_ooblayout_ecc,
|
||||
+ .free = rtl_ecc_ooblayout_free,
|
||||
+};
|
||||
+
|
||||
+static void rtl_ecc_kick_engine(struct rtl_ecc_ctx *ctx, int operation)
|
||||
+{
|
||||
+ struct rtl_ecc_engine *rtlc = ctx->rtlc;
|
||||
+
|
||||
+ regmap_write(rtlc->regmap, RTL_ECC_CFG,
|
||||
+ ctx->bch_mode | RTL_ECC_BURST_128 | RTL_ECC_DMA_PRECISE);
|
||||
+
|
||||
+ regmap_write(rtlc->regmap, RTL_ECC_DMA_START, rtlc->buf_dma);
|
||||
+ regmap_write(rtlc->regmap, RTL_ECC_DMA_TAG, rtlc->buf_dma + RTL_ECC_BLOCK_SIZE);
|
||||
+ regmap_write(rtlc->regmap, RTL_ECC_DMA_TRIGGER, operation);
|
||||
+}
|
||||
+
|
||||
+static int rtl_ecc_wait_for_engine(struct rtl_ecc_ctx *ctx)
|
||||
+{
|
||||
+ struct rtl_ecc_engine *rtlc = ctx->rtlc;
|
||||
+ int ret, status, bitflips;
|
||||
+ bool all_one;
|
||||
+
|
||||
+ /*
|
||||
+ * The ECC engine needs 6-8 us to encode/decode a BCH6 syndrome for 512 bytes of data
|
||||
+ * and 6 free bytes. In case the NAND area has been erased and all data and oob is
|
||||
+ * set to 0xff, decoding takes 30us (reason unknown). Although the engine can trigger
|
||||
+ * interrupts when finished, use active polling for now. 12 us maximum wait time has
|
||||
+ * proven to be a good tradeoff between performance and overhead.
|
||||
+ */
|
||||
+
|
||||
+ ret = regmap_read_poll_timeout(rtlc->regmap, RTL_ECC_STATUS, status,
|
||||
+ !(status & RTL_ECC_OP_STATUS), 12, 1000000);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = FIELD_GET(RTL_ECC_RESULT, status);
|
||||
+ all_one = FIELD_GET(RTL_ECC_ALL_ONE, status);
|
||||
+ bitflips = FIELD_GET(RTL_ECC_CORR_COUNT, status);
|
||||
+
|
||||
+ /* For erased blocks (all bits one) error status can be ignored */
|
||||
+ if (all_one)
|
||||
+ ret = 0;
|
||||
+
|
||||
+ return ret ? -EBADMSG : bitflips;
|
||||
+}
|
||||
+
|
||||
+static int rtl_ecc_run_engine(struct rtl_ecc_ctx *ctx, char *data, char *free,
|
||||
+ char *parity, int operation)
|
||||
+{
|
||||
+ struct rtl_ecc_engine *rtlc = ctx->rtlc;
|
||||
+ char *buf_parity = rtlc->buf + RTL_ECC_BLOCK_SIZE + RTL_ECC_FREE_SIZE;
|
||||
+ char *buf_free = rtlc->buf + RTL_ECC_BLOCK_SIZE;
|
||||
+ char *buf_data = rtlc->buf;
|
||||
+ int ret;
|
||||
+
|
||||
+ mutex_lock(&rtlc->lock);
|
||||
+
|
||||
+ memcpy(buf_data, data, RTL_ECC_BLOCK_SIZE);
|
||||
+ memcpy(buf_free, free, RTL_ECC_FREE_SIZE);
|
||||
+ memcpy(buf_parity, parity, ctx->parity_size);
|
||||
+
|
||||
+ dma_sync_single_for_device(rtlc->dev, rtlc->buf_dma, RTL_ECC_DMA_SIZE, DMA_TO_DEVICE);
|
||||
+ rtl_ecc_kick_engine(ctx, operation);
|
||||
+ ret = rtl_ecc_wait_for_engine(ctx);
|
||||
+ dma_sync_single_for_cpu(rtlc->dev, rtlc->buf_dma, RTL_ECC_DMA_SIZE, DMA_FROM_DEVICE);
|
||||
+
|
||||
+ if (ret >= 0) {
|
||||
+ memcpy(data, buf_data, RTL_ECC_BLOCK_SIZE);
|
||||
+ memcpy(free, buf_free, RTL_ECC_FREE_SIZE);
|
||||
+ memcpy(parity, buf_parity, ctx->parity_size);
|
||||
+ }
|
||||
+
|
||||
+ mutex_unlock(&rtlc->lock);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int rtl_ecc_prepare_io_req(struct nand_device *nand, struct nand_page_io_req *req)
|
||||
+{
|
||||
+ struct rtl_ecc_engine *rtlc = nand_to_rtlc(nand);
|
||||
+ struct rtl_ecc_ctx *ctx = nand_to_ctx(nand);
|
||||
+ char *data, *free, *parity;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ if (req->mode == MTD_OPS_RAW)
|
||||
+ return 0;
|
||||
+
|
||||
+ nand_ecc_tweak_req(&ctx->req_ctx, req);
|
||||
+
|
||||
+ if (req->type == NAND_PAGE_READ)
|
||||
+ return 0;
|
||||
+
|
||||
+ free = req->oobbuf.in;
|
||||
+ data = req->databuf.in;
|
||||
+ parity = req->oobbuf.in + ctx->steps * RTL_ECC_FREE_SIZE;
|
||||
+
|
||||
+ for (int i = 0; i < ctx->steps; i++) {
|
||||
+ ret |= rtl_ecc_run_engine(ctx, data, free, parity, RTL_ECC_OP_ENCODE);
|
||||
+
|
||||
+ free += RTL_ECC_FREE_SIZE;
|
||||
+ data += RTL_ECC_BLOCK_SIZE;
|
||||
+ parity += ctx->parity_size;
|
||||
+ }
|
||||
+
|
||||
+ if (unlikely(ret))
|
||||
+ dev_dbg(rtlc->dev, "ECC calculation failed\n");
|
||||
+
|
||||
+ return ret ? -EBADMSG : 0;
|
||||
+}
|
||||
+
|
||||
+static int rtl_ecc_finish_io_req(struct nand_device *nand, struct nand_page_io_req *req)
|
||||
+{
|
||||
+ struct rtl_ecc_engine *rtlc = nand_to_rtlc(nand);
|
||||
+ struct rtl_ecc_ctx *ctx = nand_to_ctx(nand);
|
||||
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
|
||||
+ char *data, *free, *parity;
|
||||
+ bool failure = false;
|
||||
+ int bitflips = 0;
|
||||
+
|
||||
+ if (req->mode == MTD_OPS_RAW)
|
||||
+ return 0;
|
||||
+
|
||||
+ if (req->type == NAND_PAGE_WRITE) {
|
||||
+ nand_ecc_restore_req(&ctx->req_ctx, req);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ free = req->oobbuf.in;
|
||||
+ data = req->databuf.in;
|
||||
+ parity = req->oobbuf.in + ctx->steps * RTL_ECC_FREE_SIZE;
|
||||
+
|
||||
+ for (int i = 0 ; i < ctx->steps; i++) {
|
||||
+ int ret = rtl_ecc_run_engine(ctx, data, free, parity, RTL_ECC_OP_DECODE);
|
||||
+
|
||||
+ if (unlikely(ret < 0))
|
||||
+ /* ECC totally fails for bitflips in erased blocks */
|
||||
+ ret = nand_check_erased_ecc_chunk(data, RTL_ECC_BLOCK_SIZE,
|
||||
+ parity, ctx->parity_size,
|
||||
+ free, RTL_ECC_FREE_SIZE,
|
||||
+ ctx->strength);
|
||||
+ if (unlikely(ret < 0)) {
|
||||
+ failure = true;
|
||||
+ mtd->ecc_stats.failed++;
|
||||
+ } else {
|
||||
+ mtd->ecc_stats.corrected += ret;
|
||||
+ bitflips = max_t(unsigned int, bitflips, ret);
|
||||
+ }
|
||||
+
|
||||
+ free += RTL_ECC_FREE_SIZE;
|
||||
+ data += RTL_ECC_BLOCK_SIZE;
|
||||
+ parity += ctx->parity_size;
|
||||
+ }
|
||||
+
|
||||
+ nand_ecc_restore_req(&ctx->req_ctx, req);
|
||||
+
|
||||
+ if (unlikely(failure))
|
||||
+ dev_dbg(rtlc->dev, "ECC correction failed\n");
|
||||
+ else if (unlikely(bitflips > 2))
|
||||
+ dev_dbg(rtlc->dev, "%d bitflips detected\n", bitflips);
|
||||
+
|
||||
+ return failure ? -EBADMSG : bitflips;
|
||||
+}
|
||||
+
|
||||
+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 ||
|
||||
+ 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);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ if (nand->ecc.user_conf.algo != NAND_ECC_ALGO_BCH ||
|
||||
+ nand->ecc.user_conf.strength != RTL_ECC_ALLOWED_STRENGTH ||
|
||||
+ nand->ecc.user_conf.placement != NAND_ECC_PLACEMENT_OOB ||
|
||||
+ nand->ecc.user_conf.step_size != RTL_ECC_BLOCK_SIZE) {
|
||||
+ dev_err(dev, "only algo=bch, strength=%d, placement=oob, step=%d supported\n",
|
||||
+ RTL_ECC_ALLOWED_STRENGTH, RTL_ECC_BLOCK_SIZE);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rtl_ecc_init_ctx(struct nand_device *nand)
|
||||
+{
|
||||
+ struct nand_ecc_props *conf = &nand->ecc.ctx.conf;
|
||||
+ struct rtl_ecc_engine *rtlc = nand_to_rtlc(nand);
|
||||
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
|
||||
+ int strength = nand->ecc.user_conf.strength;
|
||||
+ struct device *dev = nand->ecc.engine->dev;
|
||||
+ struct rtl_ecc_ctx *ctx;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = rtl_ecc_check_support(nand);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
|
||||
+ if (!ctx)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ nand->ecc.ctx.priv = ctx;
|
||||
+ mtd_set_ooblayout(mtd, &rtl_ecc_ooblayout_ops);
|
||||
+
|
||||
+ conf->algo = NAND_ECC_ALGO_BCH;
|
||||
+ conf->strength = strength;
|
||||
+ conf->step_size = RTL_ECC_BLOCK_SIZE;
|
||||
+ conf->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
|
||||
+
|
||||
+ ctx->rtlc = rtlc;
|
||||
+ ctx->steps = mtd->writesize / RTL_ECC_BLOCK_SIZE;
|
||||
+ ctx->strength = strength;
|
||||
+ ctx->bch_mode = strength == 6 ? RTL_ECC_BCH6 : RTL_ECC_BCH12;
|
||||
+ ctx->parity_size = strength == 6 ? RTL_ECC_PARITY_SIZE_BCH6 : RTL_ECC_PARITY_SIZE_BCH12;
|
||||
+
|
||||
+ ret = nand_ecc_init_req_tweaking(&ctx->req_ctx, nand);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ dev_dbg(dev, "using bch%d with geometry data=%dx%d, free=%dx6, parity=%dx%d",
|
||||
+ conf->strength, ctx->steps, conf->step_size,
|
||||
+ ctx->steps, ctx->steps, ctx->parity_size);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void rtl_ecc_cleanup_ctx(struct nand_device *nand)
|
||||
+{
|
||||
+ struct rtl_ecc_ctx *ctx = nand_to_ctx(nand);
|
||||
+
|
||||
+ if (ctx)
|
||||
+ nand_ecc_cleanup_req_tweaking(&ctx->req_ctx);
|
||||
+}
|
||||
+
|
||||
+static struct nand_ecc_engine_ops rtl_ecc_engine_ops = {
|
||||
+ .init_ctx = rtl_ecc_init_ctx,
|
||||
+ .cleanup_ctx = rtl_ecc_cleanup_ctx,
|
||||
+ .prepare_io_req = rtl_ecc_prepare_io_req,
|
||||
+ .finish_io_req = rtl_ecc_finish_io_req,
|
||||
+};
|
||||
+
|
||||
+static int rtl_ecc_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct device *dev = &pdev->dev;
|
||||
+ struct rtl_ecc_engine *rtlc;
|
||||
+ void __iomem *base;
|
||||
+ int ret;
|
||||
+
|
||||
+ rtlc = devm_kzalloc(dev, sizeof(*rtlc), GFP_KERNEL);
|
||||
+ if (!rtlc)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ base = devm_platform_ioremap_resource(pdev, 0);
|
||||
+ if (IS_ERR(base))
|
||||
+ return PTR_ERR(base);
|
||||
+
|
||||
+ ret = devm_mutex_init(dev, &rtlc->lock);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ rtlc->regmap = devm_regmap_init_mmio(dev, base, &rtl_ecc_regmap_config);
|
||||
+ if (IS_ERR(rtlc->regmap))
|
||||
+ return PTR_ERR(rtlc->regmap);
|
||||
+
|
||||
+ /*
|
||||
+ * Focus on simplicity and use a preallocated DMA buffer for data exchange with the
|
||||
+ * engine. For now make it a noncoherent memory model as invalidating/flushing caches
|
||||
+ * is faster than reading/writing uncached memory on the known architectures.
|
||||
+ */
|
||||
+
|
||||
+ rtlc->buf = dma_alloc_noncoherent(dev, RTL_ECC_DMA_SIZE, &rtlc->buf_dma,
|
||||
+ DMA_BIDIRECTIONAL, GFP_KERNEL);
|
||||
+ if (IS_ERR(rtlc->buf))
|
||||
+ return PTR_ERR(rtlc->buf);
|
||||
+
|
||||
+ rtlc->dev = dev;
|
||||
+ rtlc->engine.dev = dev;
|
||||
+ rtlc->engine.ops = &rtl_ecc_engine_ops;
|
||||
+ rtlc->engine.integration = NAND_ECC_ENGINE_INTEGRATION_EXTERNAL;
|
||||
+
|
||||
+ nand_ecc_register_on_host_hw_engine(&rtlc->engine);
|
||||
+
|
||||
+ platform_set_drvdata(pdev, rtlc);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void rtl_ecc_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct rtl_ecc_engine *rtlc = platform_get_drvdata(pdev);
|
||||
+
|
||||
+ nand_ecc_unregister_on_host_hw_engine(&rtlc->engine);
|
||||
+ dma_free_noncoherent(rtlc->dev, RTL_ECC_DMA_SIZE, rtlc->buf, rtlc->buf_dma,
|
||||
+ DMA_BIDIRECTIONAL);
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id rtl_ecc_of_ids[] = {
|
||||
+ {
|
||||
+ .compatible = "realtek,rtl9301-ecc",
|
||||
+ },
|
||||
+ { /* sentinel */ },
|
||||
+};
|
||||
+
|
||||
+static struct platform_driver rtl_ecc_driver = {
|
||||
+ .driver = {
|
||||
+ .name = "rtl-nand-ecc-engine",
|
||||
+ .of_match_table = rtl_ecc_of_ids,
|
||||
+ },
|
||||
+ .probe = rtl_ecc_probe,
|
||||
+ .remove = rtl_ecc_remove,
|
||||
+};
|
||||
+module_platform_driver(rtl_ecc_driver);
|
||||
+
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_AUTHOR("Markus Stockhausen <markus.stockhausen@gmx.de>");
|
||||
+MODULE_DESCRIPTION("Realtek NAND hardware ECC controller");
|
||||
@@ -158,6 +158,8 @@ CONFIG_MTD_CFI_ADV_OPTIONS=y
|
||||
CONFIG_MTD_CFI_GEOMETRY=y
|
||||
CONFIG_MTD_CMDLINE_PARTS=y
|
||||
CONFIG_MTD_JEDECPROBE=y
|
||||
# CONFIG_MTD_NAND_ECC_REALTEK is not set
|
||||
# CONFIG_MTD_SPI_NAND is not set
|
||||
CONFIG_MTD_SPI_NOR=y
|
||||
CONFIG_MTD_SPLIT_BRNIMAGE_FW=y
|
||||
CONFIG_MTD_SPLIT_EVA_FW=y
|
||||
|
||||
@@ -161,6 +161,8 @@ CONFIG_MTD_CFI_ADV_OPTIONS=y
|
||||
CONFIG_MTD_CFI_GEOMETRY=y
|
||||
CONFIG_MTD_CMDLINE_PARTS=y
|
||||
CONFIG_MTD_JEDECPROBE=y
|
||||
# CONFIG_MTD_NAND_ECC_REALTEK is not set
|
||||
# CONFIG_MTD_SPI_NAND is not set
|
||||
CONFIG_MTD_SPI_NOR=y
|
||||
CONFIG_MTD_SPLIT_BRNIMAGE_FW=y
|
||||
CONFIG_MTD_SPLIT_EVA_FW=y
|
||||
|
||||
@@ -145,6 +145,7 @@ CONFIG_MTD_CFI_ADV_OPTIONS=y
|
||||
CONFIG_MTD_CFI_GEOMETRY=y
|
||||
CONFIG_MTD_CMDLINE_PARTS=y
|
||||
CONFIG_MTD_JEDECPROBE=y
|
||||
# CONFIG_MTD_NAND_ECC_REALTEK is not set
|
||||
# CONFIG_MTD_SPI_NAND is not set
|
||||
CONFIG_MTD_SPI_NOR=y
|
||||
CONFIG_MTD_SPLIT_BRNIMAGE_FW=y
|
||||
|
||||
@@ -145,6 +145,7 @@ CONFIG_MTD_CFI_ADV_OPTIONS=y
|
||||
CONFIG_MTD_CFI_GEOMETRY=y
|
||||
CONFIG_MTD_CMDLINE_PARTS=y
|
||||
CONFIG_MTD_JEDECPROBE=y
|
||||
CONFIG_MTD_NAND_ECC_REALTEK=y
|
||||
CONFIG_MTD_SPI_NAND=y
|
||||
CONFIG_MTD_SPI_NOR=y
|
||||
CONFIG_MTD_SPLIT_BRNIMAGE_FW=y
|
||||
|
||||
@@ -156,6 +156,7 @@ CONFIG_MTD_CFI_ADV_OPTIONS=y
|
||||
CONFIG_MTD_CFI_GEOMETRY=y
|
||||
CONFIG_MTD_CMDLINE_PARTS=y
|
||||
CONFIG_MTD_JEDECPROBE=y
|
||||
# CONFIG_MTD_NAND_ECC_REALTEK is not set
|
||||
# CONFIG_MTD_SPI_NAND is not set
|
||||
CONFIG_MTD_SPI_NOR=y
|
||||
CONFIG_MTD_SPLIT_BRNIMAGE_FW=y
|
||||
|
||||
@@ -156,6 +156,7 @@ CONFIG_MTD_CFI_ADV_OPTIONS=y
|
||||
CONFIG_MTD_CFI_GEOMETRY=y
|
||||
CONFIG_MTD_CMDLINE_PARTS=y
|
||||
CONFIG_MTD_JEDECPROBE=y
|
||||
CONFIG_MTD_NAND_ECC_REALTEK=y
|
||||
CONFIG_MTD_SPI_NAND=y
|
||||
CONFIG_MTD_SPI_NOR=y
|
||||
CONFIG_MTD_SPLIT_BRNIMAGE_FW=y
|
||||
|
||||
Reference in New Issue
Block a user