AN889の日記

組込みエンジニアのブログ

【U-Boot】ZynqMPでInfineon製SPIフラッシュS25FLxを使うときの注意事項

XILINXプロセッサーZynq™ UltraScale+™ MPSoC(以下、ZynqMP)でInfineon製SPIフラッシュS25FLシリーズ*1を使ったときのことです。
ZynqMPとこのフラッシュとはQSPIデュアルパラレル接続しました*2

QSPIなのでフラッシュをクアッドモードに設定する必要があります。
このフラッシュのU-Bootでのクアッド設定はdrivers/mtd/spi/spi-nor-core.cspansion_read_cr_quad_enable()です。これはspi_nor_init()内で呼ばれます。ところが、クアッド設定に失敗します。

このフラッシュのクアッド設定はCR1[1](Configuration register 1のビット1 QUAD)で行います。工場出荷値は0でクアッド無効です。CR1を設定するにはWRR(Write Register)コマンド0x01を使うのですが、その際のデータ部は2バイトで、データ部の0バイト目をSR1(Status Register 1)のデータ、1バイト目をCR1のデータにします。
このデータ長が2というのが落とし穴でした。デュアルパラレル接続ではストライプでデータをフラッシュへ送信します。偶数バイトを下位フラッシュへ、奇数バイトを上位フラッシュへと *3。なので、0バイト目のSR1のデータが下位フラッシュへ、1バイト目のCR1のデータが上位デバイスへ送信され、下位・上位フラッシュそれぞれにはWRRコマンドがデータ長1になってました(バス波形でも確認)。これではSR1の設定になるので、CR1を設定できません。
なので、WRRコマンド送信時はストライプを無効にする必要があります。ストライプ有効/無効はZynqMPのGQSPI_GEN_FIFOレジスタ*4のビット18で設定します。

そこで、spansion_read_cr_quad_enable()内で呼ばれるwrite_sr_cr()でストライプ設定を無効にすることで、CR1を設定できるようになり、クアッドモードにすることができました。このパッチです。これはv2023.01_2023.1*5用です。v2023.01_2023.2*6ではストライプ設定実装が変わってるので、このパッチは適用できません。

4バイトアドレスモードにするためのset_4byte()も同様の対処必要です。このフラッシュを4バイトアドレスモードにするにはBAR[7](Bank address registerのビット7 EXTADD)を1にする必要ありますが、その設定のBRWR(Bank Register Write)コマンド0x17もストライプ有効だとデータ部が1バイトのため下位デバイスのみに送信されます。この対処もパッチに入れています。

diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index cff390b4..b58a4c43 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -2108,7 +2108,12 @@ static int write_sr_cr(struct spi_nor *nor, u8 *sr_cr)
 
    write_enable(nor);
 
+   if (nor->isparallel) /* eratta */
+       nor->spi->flags &= ~SPI_XFER_STRIPE;
    ret = nor->write_reg(nor, SPINOR_OP_WRSR, sr_cr, 2);
+   if (nor->isparallel) /* eratta */
+       nor->spi->flags |= SPI_XFER_STRIPE;
+
    if (ret < 0) {
        dev_dbg(nor->dev,
            "error while writing configuration register\n");
@@ -3656,6 +3661,38 @@ static void s25fl256l_default_init(struct spi_nor *nor)
 static struct spi_nor_fixups s25fl256l_fixups = {
    .default_init = s25fl256l_default_init,
 };
+
+static int s25fl512s_post_bfpt_fixup(struct spi_nor *nor,
+                  const struct sfdp_parameter_header *header,
+                  const struct sfdp_bfpt *bfpt,
+                  struct spi_nor_flash_parameter *params)
+{
+   int ret;
+
+   if (nor->isparallel) {
+       /* eratta */
+       nor->spi->flags &= ~SPI_XFER_STRIPE;
+       ret = set_4byte(nor, nor->info, 1);
+       nor->spi->flags |= SPI_XFER_STRIPE;
+   } else if (nor->isstacked) {
+       ret = set_4byte(nor, nor->info, 1);
+       nor->spi->flags |= SPI_XFER_U_PAGE;
+       ret |= set_4byte(nor, nor->info, 1);
+       nor->spi->flags &= ~SPI_XFER_U_PAGE;
+   } else {
+       ret = set_4byte(nor, nor->info, 1);
+   }
+   if (ret)
+       return ret;
+
+   nor->addr_width = 4;
+
+   return 0;
+}
+
+static struct spi_nor_fixups s25fl512s_fixups = {
+   .post_bfpt = s25fl512s_post_bfpt_fixup,
+};
 #endif
 
 #ifdef CONFIG_SPI_FLASH_S28HX_T
@@ -4432,6 +4469,10 @@ void spi_nor_set_fixups(struct spi_nor *nor)
    if (CONFIG_IS_ENABLED(SPI_FLASH_BAR) &&
        !strcmp(nor->info->name, "s25fl256l"))
        nor->fixups = &s25fl256l_fixups;
+
+   if (!CONFIG_IS_ENABLED(SPI_FLASH_BAR) &&
+       !strncmp(nor->info->name, "s25fl512s", 9))
+       nor->fixups = &s25fl512s_fixups;
 #endif
 
 #ifdef CONFIG_SPI_FLASH_MT35XU