Versal上Winbond stacked flash启动阶段的驱动问题

作者:Peter Zhou,AMD工程师;来源:AMD开发者社区

问题背景:近期我们在Versal VC1902上使用winbond stacked flash W25H02JV,在uboot启动的时候,发现会有概率性启动失败的问题。

围绕这个问题,我们做了一些分析,由于之前很少有项目使用此种类型的flash,所以这个型号的flash的驱动并不是很成熟。渐渐的我们发现了一些更深入的问题,现在将调试分析该问题的一些经验以Blog的方式与大家做一个分享。

首先,在uboot阶段,该flash的status register BP[3:0] bits在某种硬件因素影响的情况下会被设置成0xF,即这几个bit都被设置成了1。这样一来,在uboot启动阶段,系统就应该优先将Winbond stacked flash#1 Status Register1进行清0的操作。

请见如下第一个patch,主要需要将SPI_NOR_HAS_LOCK这个属性打开:

---
drivers/mtd/spi/spi-nor-ids.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c
index d35972106b..2544007bb8 100644
--- a/drivers/mtd/spi/spi-nor-ids.c
+++ b/drivers/mtd/spi/spi-nor-ids.c
@@ -413,7 +413,7 @@ const struct flash_info spi_nor_ids[] = {
{ INFO("w25q256", 0xef4019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("w25m512jw", 0xef6119, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("w25m512jv", 0xef7119, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
- { INFO("w25h02jv", 0xef9022, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_MULTI_DIE) },
+ { INFO("w25h02jv", 0xef9022, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_MULTI_DIE | SPI_NOR_HAS_LOCK) },

#endif
#ifdef CONFIG_SPI_FLASH_XMC
/* XMC (Wuhan Xinxin Semiconductor Manufacturing Corp.) */
--

请见如下第二个patch,在写flash SR寄存器之后,增加了delay,是为了留给硬件一定的时间去完成内部的寄存器响应:
---
drivers/mtd/spi/spi-nor-core.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index 1f0f5deaaa..bc687f33dc 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -20,6 +20,7 @@
#include
#include
#include
+#include

#include
#include
@@ -233,8 +234,13 @@ static int read_cr(struct spi_nor *nor)
*/
static int write_sr(struct spi_nor *nor, u8 val)
{
+ int ret;
nor->cmd_buf[0] = val;
- return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1);
+ ret = nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1);
+ if (JEDEC_MFR(nor->info) == SNOR_MFR_WINBOND) {
+ mdelay(20);
+ }
+ return ret;
}

/*
--

请见如下第三个patch,这些主要是为了正确处理flash multi die的逻辑。
---
arch/arm/Kconfig | 1 -
drivers/mtd/spi/sf_internal.h | 1 +
drivers/mtd/spi/spi-nor-core.c | 3 +++
drivers/spi/zynq_qspi.c | 4 +---
drivers/spi/zynqmp_gqspi.c | 3 ---
5 files changed, 5 insertions(+), 7 deletions(-)

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 90a78e3e12..fbe90875ae 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1154,7 +1154,6 @@ config ARCH_ZYNQMP
select DM_USB if USB
select FIRMWARE
select OF_CONTROL
- select SPI_FLASH_SPLIT_READ if DM_SPI_FLASH
select SPL_BOARD_INIT if SPL
select SPL_CLK if SPL
select SPL_DM if SPL
diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h
index a074338a3d..97060b7e7c 100644
--- a/drivers/mtd/spi/sf_internal.h
+++ b/drivers/mtd/spi/sf_internal.h
@@ -75,6 +75,7 @@ struct flash_info {
#define USE_CLSR BIT(14) /* use CLSR command */
#define SPI_NOR_HAS_SST26LOCK BIT(15) /* Flash supports lock/unlock via BPR */
#define SPI_NOR_OCTAL_READ BIT(16) /* Flash supports Octal Read */
+#define SPI_NOR_MULTI_DIE BIT(17) /* Flash has multi dies & need split reads*/
};

extern const struct flash_info spi_nor_ids[];
diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index 21e4300ced..e0c664278f 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -3042,6 +3042,9 @@ int spi_nor_scan(struct spi_nor *nor)
if (info->flags & SPI_NOR_NO_ERASE)
mtd->flags |= MTD_NO_ERASE;

+ if (info->flags & SPI_NOR_MULTI_DIE)
+ nor->spi->multi_die = true;
+
nor->page_size = params.page_size;
mtd->writebufsize = nor->page_size;

diff --git a/drivers/spi/zynq_qspi.c b/drivers/spi/zynq_qspi.c
index 1a103ee20f..edaeb09b3c 100644
--- a/drivers/spi/zynq_qspi.c
+++ b/drivers/spi/zynq_qspi.c
@@ -289,9 +289,7 @@ static int zynq_qspi_child_pre_probe(struct udevice *bus)
slave->option = priv->is_dual;
slave->dio = priv->is_dio;
slave->mode = plat->tx_rx_mode;
-#ifdef CONFIG_SPI_FLASH_SPLIT_READ
- slave->multi_die = 1;
-#endif
+
return 0;
}

diff --git a/drivers/spi/zynqmp_gqspi.c b/drivers/spi/zynqmp_gqspi.c
index 0a2b22ec79..21cb2c0784 100644
--- a/drivers/spi/zynqmp_gqspi.c
+++ b/drivers/spi/zynqmp_gqspi.c
@@ -442,9 +442,6 @@ static int zynqmp_qspi_child_pre_probe(struct udevice *bus)
struct zynqmp_qspi_priv *priv = dev_get_priv(bus->parent);

slave->option = priv->is_dual;
-#ifdef CONFIG_SPI_FLASH_SPLIT_READ
- slave->multi_die = 1;
-#endif
slave->bytemode = SPI_4BYTE_MODE;

return 0;
--

其次,由于该种类型的flash有多个die,每个die对应的都有status register,所以需要对每个die的status register状态进行轮询。因此需要增加如下的patch:
---
drivers/mtd/spi/spi-nor-core.c | 18 ++++++++++++++++++
include/linux/mtd/spi-nor.h | 1 +
2 files changed, 19 insertions(+)

diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index 5ea88df91b..4925d92442 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -473,8 +473,26 @@ static int spi_nor_wait_till_ready_with_timeout(struct spi_nor *nor,
return -ETIMEDOUT;
}

+static int switch_die(struct spi_nor *nor, u8 val)
+{
+ nor->cmd_buf[0] = val;
+ return nor->write_reg(nor, SPINOR_OP_DIE_SELECT, nor->cmd_buf, 1);
+}
+
static int spi_nor_wait_till_ready(struct spi_nor *nor)
{
+ int i, ret;
+
+ if(JEDEC_MFR(nor->info) == SNOR_MFR_WINBOND)
+ {
+ for (i=0; i< 4; i++) {
+ switch_die(nor, i);
+ ret = spi_nor_wait_till_ready_with_timeout(nor,
+ DEFAULT_READY_WAIT_JIFFIES);
+ if (ret)
+ ret;
+ }
+ }
return spi_nor_wait_till_ready_with_timeout(nor,
DEFAULT_READY_WAIT_JIFFIES);
}
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index fa9b6ced0c..f0645aa286 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -67,6 +67,7 @@
#define SPINOR_OP_CLFSR 0x50 /* Clear flag status register */
#define SPINOR_OP_RDEAR 0xc8 /* Read Extended Address Register */
#define SPINOR_OP_WREAR 0xc5 /* Write Extended Address Register */
+#define SPINOR_OP_DIE_SELECT 0xc2 /* Software Die Select Register */

/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
#define SPINOR_OP_READ_4B 0x13 /* Read data bytes (low frequency) */
--

然后,在flash进行下一步操作之前,无论是在活动中的die或者是非活动中的die,都需要保证上一次的flash操作已经完成了。那么如何来保证这一点呢?我们可以通过监视WIP bit和WEL bit来完成这些。因此,我们需要增加如下的patch:
---
drivers/mtd/spi/spi-nor-core.c | 11 +++++++++++
1 file changed, 11 insertions(+)

diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index cff390b471..30221c5bd5 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -781,6 +781,17 @@ static int spi_nor_sr_ready(struct spi_nor *nor)
nor->write_reg(nor, SPINOR_OP_CLSR, NULL, 0);
return -EIO;
}
+ /*
+ * In Winbond flashes with multi die, all active/inactive die are
+ * targeted during Non-Volatile Write Status Register (1, 2, & 3),
+ * Write EAR, Program/Erase Security Register or Chip Erase operation.
+ * So after each of the above operation we need to wait for the
+ * operation cycle to complete before performing the next operation.
+ * This is achived by monitering the WIP bit of the ative die & the WEL
+ * bit in the Status Register-1.
+ */
+ if(JEDEC_MFR(nor->info) == SNOR_MFR_WINBOND)
+ return !(sr & (SR_WIP | SR_WEL);

return !(sr & SR_WIP);
}
--

最后,由于在spi_nor_init函数中初始化flash的时候,我们没有初始化upper flash,这将导致upper flash没有以4字节地址的方式进行初始化,并且也没有enable quad模式,所以我们需要增加patch来正确初始化upper flash:

---
drivers/mtd/spi/spi-nor-core.c | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index 48cf25216d..f6fced7c7e 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -2781,6 +2781,14 @@ int spi_nor_scan(struct spi_nor *nor)
if (ret)
return ret;

+ if (nor->isstacked) {
+ nor->spi->flags |= SPI_XFER_U_PAGE;
+ ret = spi_nor_init(nor);
+ if (ret)
+ return ret;
+ nor->spi->flags &= ~SPI_XFER_U_PAGE;
+ }
+
nor->name = mtd->name;
nor->size = mtd->size;
nor->erase_size = mtd->erasesize;
--

完成以上patch后,重新编译uboot,我们即可在Versal VC1902上使用winbond stacked flash W25H02JV来正确运行uboot了,linux也能运行起来了。

最后需要注意的是:以上问题的解决方案,将在2023.2 release版本中包含。

最新文章

最新文章