XILINX製プロセッサーZynq™ UltraScale+™ MPSoC(以下、ZynqMP)でInfineon製SPIフラッシュS25FLシリーズ*1を使ったときのことです。
ZynqMPとこのフラッシュとはQSPIデュアルパラレル接続しました*2。
QSPIなのでフラッシュをクアッドモードに設定する必要があります。
このフラッシュのU-Bootでのクアッド設定はdrivers/mtd/spi/spi-nor-core.c
のspansion_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
*1:https://www.infineon.com/dgdl/Infineon-S25FL512S_512_Mb(64_MB)3.0_V_SPI_Flash_Memory-DataSheet-v20_00-EN.pdf?fileId=8ac78c8c7d0d8da4017d0ed046ae4b53
*2:https://docs.xilinx.com/r/en-US/ug1085-zynq-ultrascale-trm/Two-SPI-Flash-Memories-with-Separate-Buses-Dual-Parallel
*3:https://docs.xilinx.com/r/en-US/ug1085-zynq-ultrascale-trm/Data-Arrangement
*4:https://docs.xilinx.com/r/en-US/ug1087-zynq-ultrascale-registers/GQSPI_GEN_FIFO-QSPI-Register
*5:https://github.com/Xilinx/u-boot-xlnx/tree/xlnx_rebase_v2023.01_2023.1/
*6:https://github.com/Xilinx/u-boot-xlnx/tree/xlnx_rebase_v2023.01_2023.2/