MT7621 soc HW watchdog does not work?

SPI_NOR_4B_OPCODES is not define in this linux version code

So you have to backport it..

I've porting this code to linux 4.4.198,but the system still not reboot and huang after HW watchdog time out。

Try triggering a kernel panic to check if it reboots properly.
echo c > /proc/sysrq-trigger

reboot failed.hang

i add this code

and software reboot can become effective.but watchdog timeout still hang.

That means the flash chip is still in stateful 4-byte mode. Your backport does not work.

so you mean the flash use 3-byte mode all the time.then watchdog timeout will reboot automatic ?

I changed to linux 4.14.206 source code which has the patch stateful 4-byte mode。
patch:0053-mtd-spi-nor-add-w25q256-3b-mode-switch、0054-mtd-spi-nor-w25q256-respect-default-mode
software reboot cmd is effective.but watchdog timeout still hang.I think the key problem is not spi nor flash。

You still need to set SPI_NOR_4B_OPCODES

but mt7621 boot HW strapping is SPI 3-byte address mode.

anyhow i change HW spi nor flash into 16M,the watchdog still not work.

SPI_NOR_4B_OPCODES is stateless and not a mode

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.