H7 QSPI Transfer Error
QSPI transfer error flag (TEF) is set while reading a certain block in qspi_read_cmd_qaddr_qdata this happens exactly when the host (in this case arch/Linux) mounts the storage. I tested a lot of things (disabling cache, IRQs, changing timing etc..) nothing works except if I clear the error flag and restart the transfer it works. There must be something different in the way the host reads/requests blocks because this issue does Not happen on Windows. Unfortunately I don't have any other board with a QSPI flash to test with, the flash I'm using right now is W25Q256JV. This is gdb backtrace from where it gets stuck, thought it might be helpful.
EDIT: forgot to mention that the QSPI is used for the internal storage/filesystem.
0x0813e9e6 in qspi_read_cmd_qaddr_qdata (self_in=0x81dc220 <qspi_bus>, cmd=235 '\353', addr=16642048, len=4096,
dest=0x2407ac00 '\377' <repeats 200 times>...) at qspi.c:293
293 while (!(QUADSPI->SR & QUADSPI_SR_FTF)) {
(gdb) backtrace
#0 0x0813e9e6 in qspi_read_cmd_qaddr_qdata (self_in=0x81dc220 <qspi_bus>, cmd=235 '\353', addr=16642048, len=4096,
dest=0x2407ac00 '\377' <repeats 200 times>...) at qspi.c:293
#1 0x081956c6 in mp_spiflash_read_data (self=0x30003d4c <spi_bdev>, addr=16642048, len=4096,
dest=0x2407ac00 '\377' <repeats 200 times>...) at ../../drivers/memory/spiflash.c:117
#2 0x08195a70 in mp_spiflash_cached_read (self=0x30003d4c <spi_bdev>, addr=16642048, len=4096,
dest=0x2407ac00 '\377' <repeats 200 times>...) at ../../drivers/memory/spiflash.c:316
#3 0x08137d38 in spi_bdev_readblocks (bdev=0x30003d4c <spi_bdev>, dest=0x2407ac00 '\377' <repeats 200 times>..., block_num=32504,
num_blocks=8) at spibdev.c:64
#4 0x0813818e in storage_read_blocks (dest=0x2407ac00 '\377' <repeats 200 times>..., block_num=32760, num_blocks=8) at storage.c:192
#5 0x08136bca in FLASH_STORAGE_Read (lun=0 '\000', buf=0x2407ac00 '\377' <repeats 200 times>..., blk_addr=32760, blk_len=8)
at usbd_msc_storage.c:136
#6 0x08171478 in SCSI_ProcessRead (pdev=0x300016e4 <usb_device+4>, lun=0 '\000') at usbdev/class/src/usbd_msc_scsi.c:716
#7 0x081711e6 in SCSI_Read10 (pdev=0x300016e4 <usb_device+4>, lun=0 '\000', params=0x3000192f <usb_device+591> "(")
at usbdev/class/src/usbd_msc_scsi.c:552
#8 0x08170b02 in SCSI_ProcessCmd (pdev=0x300016e4 <usb_device+4>, lun=0 '\000', params=0x3000192f <usb_device+591> "(")
at usbdev/class/src/usbd_msc_scsi.c:166
#9 0x08170836 in MSC_BOT_CBW_Decode (pdev=0x300016e4 <usb_device+4>) at usbdev/class/src/usbd_msc_bot.c:257
#10 0x0817078e in MSC_BOT_DataOut (pdev=0x300016e4 <usb_device+4>, epnum=3 '\003') at usbdev/class/src/usbd_msc_bot.c:207
#11 0x081703de in USBD_CDC_MSC_HID_DataOut (pdev=0x300016e4 <usb_device+4>, epnum=3 '\003') at usbdev/class/src/usbd_cdc_msc_hid.c:957
#12 0x0816ed3c in USBD_LL_DataOutStage (pdev=0x300016e4 <usb_device+4>, epnum=3 '\003', pdata=0x3000193f <usb_device+607> "\313USBS\"")
at usbdev/core/src/usbd_core.c:330
#13 0x0813580a in HAL_PCD_DataOutStageCallback (hpcd=0x30003844 <pcd_fs_handle>, epnum=3 '\003') at usbd_conf.c:211
#14 0x08050a8c in PCD_EP_OutXfrComplete_int (hpcd=0x30003844 <pcd_fs_handle>, epnum=3) at src/stm32h7xx_hal_pcd.c:2152
#15 0x0804fbc0 in HAL_PCD_IRQHandler (hpcd=0x30003844 <pcd_fs_handle>) at src/stm32h7xx_hal_pcd.c:1077
#16 0x08135242 in OTG_FS_IRQHandler () at stm32_it.c:311
#17 <signal handler called>
#18 mp_hal_stdin_rx_chr () at mphalport.c:23
#19 0x08155e0e in readline (line=0x3003fcd0, prompt=0x81a5b8c ">>> ") at ../../lib/mp-readline/readline.c:425
#20 0x081496d6 in pyexec_friendly_repl () at ../../lib/utils/pyexec.c:476
#21 0x08066184 in main () at main.c:656
(gdb) p /x *QUADSPI
$1 = {CR = 0x311, DCR = 0x170100, SR = 0x1, FCR = 0x0, DLR = 0xfff, CCR = 0x710edeb, AR = 0xfdf000, ABR = 0x0, DR = 0x0, PSMKR = 0x0,
PSMAR = 0x0, PIR = 0x0, LPTR = 0x0}
Note SR=0x1
Edit2: Clearing the flag and setting the address again allows the transfer to finish:
295 if (((uintptr_t)dest & 3) == 0) {
1: /x *QUADSPI = {CR = 0x311, DCR = 0x170100, SR = 0x1, FCR = 0x0, DLR = 0xfff, CCR = 0x710edeb, AR = 0xfdf000, ABR = 0x0, DR = 0x0,
PSMKR = 0x0, PSMAR = 0x0, PIR = 0x0, LPTR = 0x0}
(gdb) set QUADSPI->FCR |= QUADSPI_FCR_CTEF
(gdb) set QUADSPI->AR=0xfdf000
(gdb) p /x *QUADSPI
$4 = {CR = 0x311, DCR = 0x170100, SR = 0x1f24, FCR = 0x0, DLR = 0xfff, CCR = 0x710edeb, AR = 0xfdf000, ABR = 0x0, DR = 0xffffffff,
PSMKR = 0x0, PSMAR = 0x0, PIR = 0x0, LPTR = 0x0}
Edit 3: So this makes it work, meaning I don't get a TEF if I do the following:
QUADSPI->CR &= ~(1 << QUADSPI_CR_EN_Pos);
QUADSPI->AR = addr; // addres to read from
QUADSPI->CR |= (1 << QUADSPI_CR_EN_Pos);
QUADSPI->AR = addr; // re-write address
WIP: stm32/qspi: Detect transfer errors and pass back up to spiflash driver.
This is an initial attempt to catch and pass up errors in QSPI Flash transfers.
It only has support for stm32 qspi driver.
The regular SPI driver should also likely be extended to pass up errors (if these are detectable).
I haven't looked at what's needed to make other ports compatible with this.
Also, qspi_read_cmd() was already directly returning the read value as a uint32_t. Returning an error code instead of this is not a good way to handle this, likely the return value should be the error code like other functions here now have and the read value passed back in a pointer.