MT7621 soc HW watchdog does not work?

i enable SPI_NOR_4B_OPCODES in my code。the result the same

Then you probably won't need the two (0053,54) patches.

patch 0053:
mtd: spi-nor: add support for switching between 3-byte and 4-byte addressing on w25q256 flash

On some devices the flash chip needs to be in 3-byte addressing mode during
reboot, otherwise the boot loader will fail to start.
This mode however does not allow regular reads/writes onto the upper 16M
half. W25Q256 has separate read commands for reading from >16M, however
it does not have any separate write commands.
This patch changes the code to leave the chip in 3-byte mode most of the
time and only switch during erase/write cycles that go to >16M
addresses.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -89,6 +89,10 @@ struct flash_info {
 #define NO_CHIP_ERASE		BIT(12) /* Chip does not support chip erase */
 #define SPI_NOR_SKIP_SFDP	BIT(13)	/* Skip parsing of SFDP tables */
 #define USE_CLSR		BIT(14)	/* use CLSR command */
+#define SPI_NOR_4B_READ_OP	BIT(15)	/*
+					 * Like SPI_NOR_4B_OPCODES, but for read
+					 * op code only.
+					 */
 
 	int	(*quad_enable)(struct spi_nor *nor);
 };
@@ -242,6 +246,15 @@ static inline u8 spi_nor_convert_3to4_er
 				      ARRAY_SIZE(spi_nor_3to4_erase));
 }
 
+static void spi_nor_set_4byte_read(struct spi_nor *nor,
+				   const struct flash_info *info)
+{
+	nor->addr_width = 3;
+	nor->ext_addr = 0;
+	nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
+	nor->flags |= SNOR_F_4B_EXT_ADDR;
+}
+
 static void spi_nor_set_4byte_opcodes(struct spi_nor *nor,
 				      const struct flash_info *info)
 {
@@ -469,6 +482,36 @@ static int spi_nor_erase_sector(struct s
 	return nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width);
 }
 
+static int spi_nor_check_ext_addr(struct spi_nor *nor, u32 addr)
+{
+	bool ext_addr;
+	int ret;
+	u8 cmd;
+
+	if (!(nor->flags & SNOR_F_4B_EXT_ADDR))
+		return 0;
+
+	ext_addr = !!(addr & 0xff000000);
+	if (nor->ext_addr == ext_addr)
+		return 0;
+
+	cmd = ext_addr ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;
+	write_enable(nor);
+	ret = nor->write_reg(nor, cmd, NULL, 0);
+	if (ret)
+		return ret;
+
+	cmd = 0;
+	ret = nor->write_reg(nor, SPINOR_OP_WREAR, &cmd, 1);
+	if (ret)
+		return ret;
+
+	nor->addr_width = 3 + ext_addr;
+	nor->ext_addr = ext_addr;
+	write_disable(nor);
+	return 0;
+}
+
 /*
  * Erase an address range on the nor chip.  The address range may extend
  * one or more erase sectors.  Return an error is there is a problem erasing.
@@ -494,6 +537,10 @@ static int spi_nor_erase(struct mtd_info
 	if (ret)
 		return ret;
 
+	ret = spi_nor_check_ext_addr(nor, addr + len);
+	if (ret)
+		return ret;
+
 	/* whole-chip erase? */
 	if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
 		unsigned long timeout;
@@ -544,6 +591,7 @@ static int spi_nor_erase(struct mtd_info
 	write_disable(nor);
 
 erase_err:
+	spi_nor_check_ext_addr(nor, 0);
 	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
 
 	instr->state = ret ? MTD_ERASE_FAILED : MTD_ERASE_DONE;
@@ -836,7 +884,9 @@ static int spi_nor_lock(struct mtd_info
 	if (ret)
 		return ret;
 
+	spi_nor_check_ext_addr(nor, ofs + len);
 	ret = nor->flash_lock(nor, ofs, len);
+	spi_nor_check_ext_addr(nor, 0);
 
 	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_UNLOCK);
 	return ret;
@@ -851,7 +901,9 @@ static int spi_nor_unlock(struct mtd_inf
 	if (ret)
 		return ret;
 
+	spi_nor_check_ext_addr(nor, ofs + len);
 	ret = nor->flash_unlock(nor, ofs, len);
+	spi_nor_check_ext_addr(nor, 0);
 
 	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);
 	return ret;
@@ -1192,7 +1244,7 @@ static const struct flash_info spi_nor_i
 	{ "w25q80", INFO(0xef5014, 0, 64 * 1024,  16, SECT_4K) },
 	{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024,  16, SECT_4K) },
 	{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
-	{ "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+	{ "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_READ_OP) },
 	{ "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024,
 			SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ) },
 
@@ -1252,6 +1304,9 @@ static int spi_nor_read(struct mtd_info
 	if (ret)
 		return ret;
 
+	if (nor->flags & SNOR_F_4B_EXT_ADDR)
+		nor->addr_width = 4;
+
 	while (len) {
 		loff_t addr = from;
 
@@ -1276,6 +1331,18 @@ static int spi_nor_read(struct mtd_info
 	ret = 0;
 
 read_err:
+	if (nor->flags & SNOR_F_4B_EXT_ADDR) {
+		u8 val = 0;
+
+		if ((from + len) & 0xff000000) {
+			write_enable(nor);
+			nor->write_reg(nor, SPINOR_OP_WREAR, &val, 1);
+			write_disable(nor);
+		}
+
+		nor->addr_width = 3;
+	}
+
 	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ);
 	return ret;
 }
@@ -1377,6 +1444,10 @@ static int spi_nor_write(struct mtd_info
 	if (ret)
 		return ret;
 
+	ret = spi_nor_check_ext_addr(nor, to + len);
+	if (ret < 0)
+		return ret;
+
 	for (i = 0; i < len; ) {
 		ssize_t written;
 		loff_t addr = to + i;
@@ -1417,6 +1488,7 @@ static int spi_nor_write(struct mtd_info
 	}
 
 write_err:
+	spi_nor_check_ext_addr(nor, 0);
 	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
 	return ret;
 }
@@ -2842,8 +2914,10 @@ int spi_nor_scan(struct spi_nor *nor, co
 	} else if (mtd->size > 0x1000000) {
 		/* enable 4-byte addressing if the device exceeds 16MiB */
 		nor->addr_width = 4;
-		if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
-		    info->flags & SPI_NOR_4B_OPCODES)
+		if (info->flags & SPI_NOR_4B_READ_OP)
+			spi_nor_set_4byte_read(nor, info);
+		else if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
+			 info->flags & SPI_NOR_4B_OPCODES)
 			spi_nor_set_4byte_opcodes(nor, info);
 		else
 			set_4byte(nor, info, 1);
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -102,6 +102,7 @@
 /* Used for Macronix and Winbond flashes. */
 #define SPINOR_OP_EN4B		0xb7	/* Enter 4-byte mode */
 #define SPINOR_OP_EX4B		0xe9	/* Exit 4-byte mode */
+#define SPINOR_OP_WREAR		0xc5	/* Write extended address register */
 
 /* Used for Spansion flashes only. */
 #define SPINOR_OP_BRWR		0x17	/* Bank register write */
@@ -229,6 +230,7 @@ enum spi_nor_option_flags {
 	SNOR_F_S3AN_ADDR_DEFAULT = BIT(3),
 	SNOR_F_READY_XSR_RDY	= BIT(4),
 	SNOR_F_USE_CLSR		= BIT(5),
+	SNOR_F_4B_EXT_ADDR	= BIT(6),
 };
 
 /**
@@ -280,6 +282,7 @@ struct spi_nor {
 	enum spi_nor_protocol	reg_proto;
 	bool			sst_write_second;
 	u32			flags;
+	u8			ext_addr;
 	u8			cmd_buf[SPI_NOR_MAX_CMD_SIZE];
 
 	int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
patch 54:
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -144,20 +144,29 @@ static int read_fsr(struct spi_nor *nor)
  * location. Return the configuration register value.
  * Returns negative if error occurred.
  */
-static int read_cr(struct spi_nor *nor)
+static int _read_cr(struct spi_nor *nor, u8 reg)
 {
 	int ret;
 	u8 val;
 
-	ret = nor->read_reg(nor, SPINOR_OP_RDCR, &val, 1);
+	ret = nor->read_reg(nor, reg, &val, 1);
 	if (ret < 0) {
-		dev_err(nor->dev, "error %d reading CR\n", ret);
+		dev_err(nor->dev, "error %d reading %s\n", ret,
+			(reg==SPINOR_OP_RDCR)?"CR":"XCR");
 		return ret;
 	}
 
 	return val;
 }
 
+static inline int read_cr(struct spi_nor *nor) {
+	return _read_cr(nor, SPINOR_OP_RDCR);
+}
+
+static inline int read_xcr(struct spi_nor *nor) {
+	return _read_cr(nor, SPINOR_OP_RDXCR);
+}
+
 /*
  * Write status register 1 byte
  * Returns negative if error occurred.
@@ -2914,9 +2923,16 @@ int spi_nor_scan(struct spi_nor *nor, co
 	} else if (mtd->size > 0x1000000) {
 		/* enable 4-byte addressing if the device exceeds 16MiB */
 		nor->addr_width = 4;
-		if (info->flags & SPI_NOR_4B_READ_OP)
-			spi_nor_set_4byte_read(nor, info);
-		else if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
+		if (info->flags & SPI_NOR_4B_READ_OP) {
+			if (JEDEC_MFR(info) == SNOR_MFR_WINBOND) {
+				ret = read_xcr(nor);
+				if (!(ret > 0 && (ret & XCR_DEF_4B_ADDR_MODE)))
+					spi_nor_set_4byte_read(nor, info);
+				else
+					set_4byte(nor, info, 1);
+			} else
+				spi_nor_set_4byte_read(nor, info);
+		} else if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
 			 info->flags & SPI_NOR_4B_OPCODES)
 			spi_nor_set_4byte_opcodes(nor, info);
 		else
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -103,6 +103,7 @@
 #define SPINOR_OP_EN4B		0xb7	/* Enter 4-byte mode */
 #define SPINOR_OP_EX4B		0xe9	/* Exit 4-byte mode */
 #define SPINOR_OP_WREAR		0xc5	/* Write extended address register */
+#define SPINOR_OP_RDXCR		0x15	/* Read extended configuration register */
 
 /* Used for Spansion flashes only. */
 #define SPINOR_OP_BRWR		0x17	/* Bank register write */
@@ -135,6 +136,7 @@
 
 /* Configuration Register bits. */
 #define CR_QUAD_EN_SPAN		BIT(1)	/* Spansion Quad I/O */
+#define XCR_DEF_4B_ADDR_MODE	BIT(1)	/* Winbond 4B mode default */
 
 /* Status Register 2 bits. */
 #define SR2_QUAD_EN_BIT7	BIT(7)

i changed both SPI_NOR_4B_READ_OP or SPI_NOR_4B_OPCODES at code:
{ "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_READ_OP) },

the watchdog hang both。

i tested use 3byte mode in spi nor flash,the watchdog is ok and system reboot successfully。

1 Like

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.