mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-27 09:12:39 +00:00
173 lines
5.7 KiB
Diff
173 lines
5.7 KiB
Diff
|
From bd568cc04c675b7fa97214d278a54794c2ecc2ad Mon Sep 17 00:00:00 2001
|
||
|
From: Reto Schneider <reto.schneider@husqvarnagroup.com>
|
||
|
Date: Thu, 11 Feb 2021 12:36:19 +0100
|
||
|
Subject: [PATCH] mtd: spinand: gigadevice: Support GD5F1GQ5UExxG
|
||
|
|
||
|
The relevant changes to the already existing GD5F1GQ4UExxG support has
|
||
|
been determined by consulting the GigaDevice product change notice
|
||
|
AN-0392-10, version 1.0 from November 30, 2020.
|
||
|
|
||
|
As the overlaps are huge, variable names have been generalized
|
||
|
accordingly.
|
||
|
|
||
|
Apart from the lowered ECC strength (4 instead of 8 bits per 512 bytes),
|
||
|
the new device ID, and the extra quad IO dummy byte, no changes had to
|
||
|
be taken into account.
|
||
|
|
||
|
New hardware features are not supported, namely:
|
||
|
- Power on reset
|
||
|
- Unique ID
|
||
|
- Double transfer rate (DTR)
|
||
|
- Parameter page
|
||
|
- Random data quad IO
|
||
|
|
||
|
The inverted semantic of the "driver strength" register bits, defaulting
|
||
|
to 100% instead of 50% for the Q5 devices, got ignored as the driver has
|
||
|
never touched them anyway.
|
||
|
|
||
|
The no longer supported "read from cache during block erase"
|
||
|
functionality is not reflected as the current SPI NAND core does not
|
||
|
support it anyway.
|
||
|
|
||
|
Implementation has been tested on MediaTek MT7688 based GARDENA smart
|
||
|
Gateways using both, GigaDevice GD5F1GQ5UEYIG and GD5F1GQ4UBYIG.
|
||
|
|
||
|
Signed-off-by: Reto Schneider <reto.schneider@husqvarnagroup.com>
|
||
|
Reviewed-by: Frieder Schrempf <frieder.schrempf@kontron.de>
|
||
|
Reviewed-by: Stefan Roese <sr@denx.de>
|
||
|
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
|
||
|
Link: https://lore.kernel.org/linux-mtd/20210211113619.3502-1-code@reto-schneider.ch
|
||
|
(cherry picked from commit 469b992489852b500d39048aa0013639dfe9f2e6)
|
||
|
---
|
||
|
drivers/mtd/nand/spi/gigadevice.c | 69 +++++++++++++++++++++++++++----
|
||
|
1 file changed, 60 insertions(+), 9 deletions(-)
|
||
|
|
||
|
--- a/drivers/mtd/nand/spi/gigadevice.c
|
||
|
+++ b/drivers/mtd/nand/spi/gigadevice.c
|
||
|
@@ -13,7 +13,10 @@
|
||
|
#define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS (1 << 4)
|
||
|
#define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS (3 << 4)
|
||
|
|
||
|
-#define GD5FXGQ4UEXXG_REG_STATUS2 0xf0
|
||
|
+#define GD5FXGQ5XE_STATUS_ECC_1_4_BITFLIPS (1 << 4)
|
||
|
+#define GD5FXGQ5XE_STATUS_ECC_4_BITFLIPS (3 << 4)
|
||
|
+
|
||
|
+#define GD5FXGQXXEXXG_REG_STATUS2 0xf0
|
||
|
|
||
|
#define GD5FXGQ4UXFXXG_STATUS_ECC_MASK (7 << 4)
|
||
|
#define GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS (0 << 4)
|
||
|
@@ -102,7 +105,7 @@ static int gd5fxgq4xa_ecc_get_status(str
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
-static int gd5fxgq4_variant2_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||
|
+static int gd5fxgqx_variant2_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||
|
struct mtd_oob_region *region)
|
||
|
{
|
||
|
if (section)
|
||
|
@@ -114,7 +117,7 @@ static int gd5fxgq4_variant2_ooblayout_e
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
-static int gd5fxgq4_variant2_ooblayout_free(struct mtd_info *mtd, int section,
|
||
|
+static int gd5fxgqx_variant2_ooblayout_free(struct mtd_info *mtd, int section,
|
||
|
struct mtd_oob_region *region)
|
||
|
{
|
||
|
if (section)
|
||
|
@@ -127,9 +130,10 @@ static int gd5fxgq4_variant2_ooblayout_f
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
-static const struct mtd_ooblayout_ops gd5fxgq4_variant2_ooblayout = {
|
||
|
- .ecc = gd5fxgq4_variant2_ooblayout_ecc,
|
||
|
- .free = gd5fxgq4_variant2_ooblayout_free,
|
||
|
+/* Valid for Q4/Q5 and Q6 (untested) devices */
|
||
|
+static const struct mtd_ooblayout_ops gd5fxgqx_variant2_ooblayout = {
|
||
|
+ .ecc = gd5fxgqx_variant2_ooblayout_ecc,
|
||
|
+ .free = gd5fxgqx_variant2_ooblayout_free,
|
||
|
};
|
||
|
|
||
|
static int gd5fxgq4xc_ooblayout_256_ecc(struct mtd_info *mtd, int section,
|
||
|
@@ -165,7 +169,7 @@ static int gd5fxgq4uexxg_ecc_get_status(
|
||
|
u8 status)
|
||
|
{
|
||
|
u8 status2;
|
||
|
- struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQ4UEXXG_REG_STATUS2,
|
||
|
+ struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQXXEXXG_REG_STATUS2,
|
||
|
&status2);
|
||
|
int ret;
|
||
|
|
||
|
@@ -203,6 +207,43 @@ static int gd5fxgq4uexxg_ecc_get_status(
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
+static int gd5fxgq5xexxg_ecc_get_status(struct spinand_device *spinand,
|
||
|
+ u8 status)
|
||
|
+{
|
||
|
+ u8 status2;
|
||
|
+ struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQXXEXXG_REG_STATUS2,
|
||
|
+ &status2);
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ switch (status & STATUS_ECC_MASK) {
|
||
|
+ case STATUS_ECC_NO_BITFLIPS:
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ case GD5FXGQ5XE_STATUS_ECC_1_4_BITFLIPS:
|
||
|
+ /*
|
||
|
+ * Read status2 register to determine a more fine grained
|
||
|
+ * bit error status
|
||
|
+ */
|
||
|
+ ret = spi_mem_exec_op(spinand->spimem, &op);
|
||
|
+ if (ret)
|
||
|
+ return ret;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * 1 ... 4 bits are flipped (and corrected)
|
||
|
+ */
|
||
|
+ /* bits sorted this way (1...0): ECCSE1, ECCSE0 */
|
||
|
+ return ((status2 & STATUS_ECC_MASK) >> 4) + 1;
|
||
|
+
|
||
|
+ case STATUS_ECC_UNCOR_ERROR:
|
||
|
+ return -EBADMSG;
|
||
|
+
|
||
|
+ default:
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ return -EINVAL;
|
||
|
+}
|
||
|
+
|
||
|
static int gd5fxgq4ufxxg_ecc_get_status(struct spinand_device *spinand,
|
||
|
u8 status)
|
||
|
{
|
||
|
@@ -282,7 +323,7 @@ static const struct spinand_info gigadev
|
||
|
&write_cache_variants,
|
||
|
&update_cache_variants),
|
||
|
SPINAND_HAS_QE_BIT,
|
||
|
- SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout,
|
||
|
+ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
|
||
|
gd5fxgq4uexxg_ecc_get_status)),
|
||
|
SPINAND_INFO("GD5F1GQ4UFxxG",
|
||
|
SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48),
|
||
|
@@ -292,8 +333,18 @@ static const struct spinand_info gigadev
|
||
|
&write_cache_variants,
|
||
|
&update_cache_variants),
|
||
|
SPINAND_HAS_QE_BIT,
|
||
|
- SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout,
|
||
|
+ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
|
||
|
gd5fxgq4ufxxg_ecc_get_status)),
|
||
|
+ SPINAND_INFO("GD5F1GQ5UExxG",
|
||
|
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x51),
|
||
|
+ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
|
||
|
+ NAND_ECCREQ(4, 512),
|
||
|
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||
|
+ &write_cache_variants,
|
||
|
+ &update_cache_variants),
|
||
|
+ SPINAND_HAS_QE_BIT,
|
||
|
+ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
|
||
|
+ gd5fxgq5xexxg_ecc_get_status)),
|
||
|
};
|
||
|
|
||
|
static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = {
|