zephyrproject-rtos / zephyr

Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
https://docs.zephyrproject.org
Apache License 2.0
10.48k stars 6.42k forks source link

stm32-sdmmc doesn't work when CONFIG_PM is enabled #77526

Open bobobo1618 opened 2 weeks ago

bobobo1618 commented 2 weeks ago

Describe the bug

Attempts to use stm32-sdmmc in various ways have failed.

To Reproduce

  1. Connect an Adafruit SDIO/SPI breakout to the SDMMC pins of a NUCLEO-U575ZI-Q or NUCLEO-H743ZI2. Nothing fancy, just electrically connect the relevant signal pins on the one to the other. I'm connecting CLK, CMD, D[0-3], GND and 3V3. The only unconnected pin is DET. In my case I've used a piece of strip board to wire the pins to a new header matching the ST Zio pinout so that the module can be connected to the board without long conductors. Note that the Adafruit module includes decoupling capacitors for power and 47kΩ pullups on all data lines.
  2. Update the sdmmc1 node in Device Tree like so:
    &sdmmc1 {
        status = "okay";
        pinctrl-0 = <
            &sdmmc1_d0_pc8
            &sdmmc1_d1_pc9
            &sdmmc1_d2_pc10
            &sdmmc1_d3_pc11
            &sdmmc1_ck_pc12
            &sdmmc1_cmd_pd2>;
        clk-div = <4>;
        bus-width = <4>;
        pinctrl-names = "default";
    };

    For NUCLEO-H743ZI2, it's also necessary to update the clock, as by default the clock is not 48MHz but 480MHz, which causes an error at runtime:

    clocks = <&rcc STM32_CLOCK_BUS_AHB3 0x00010000>,
            <&rcc STM32_SRC_HSI48 SDMMC_SEL(0)>;
  3. Attempt to mount then read or write the SD card. A minimal snippet from my larger program that does this:

    static FATFS fat_fs;
    struct fs_file_t logfile;
    /* mounting info */
    static struct fs_mount_t mp = {
        .type = FS_FATFS,
        .fs_data = &fat_fs,
        .mnt_point = "/SD:",
    };
    
    int ret = fs_mount(&mp);
    
    if (ret == FR_OK) {
      LOG_INF("Successfully mounted SD");
    } else {
      LOG_ERR("Failed to mount SD: %d", ret);
      return;
    }
    
    fs_file_t_init(&logfile);
    static const char *file_path = "/SD:/test.log.bin";
    ret = fs_open(&logfile, file_path, FS_O_CREATE | FS_O_TRUNC | FS_O_WRITE);
    if (ret) {
      LOG_ERR("Failed to open %s: %d", file_path, ret);
      return;
    }
    while(1) {
      ret = fs_write(&logfile, "test", 4);
      if (ret < 0) {
        LOG_ERR("Couldn't write to log file: %d", ret);
      }
      if (ret < 4) {
        LOG_ERR("Couldn't finish write: %d", ret);
      }
    }

Expected behavior Mount, reads and writes should all work without any problems.

Impact Showstopper for me for the STM32 series, works fine on other MCUs/boards (e.g. ATSAMD21 via SPI, LPC55S69 via SDMMC).

Logs and console output

STM32U5:

[00:00:03.001,000] <err> stm32_sdmmc: sd read error 32
[00:00:03.001,000] <err> fs: fs mount error (-5)
[00:00:03.001,000] <err> main: Failed to mount SD: -5

STM32H7:

[00:00:01.011,000] <inf> main: Successfully mounted SD
[ Various other things from this program happen ]
[00:00:01.553,000] <err> stm32_sdmmc: sd write error 12
[00:00:01.553,000] <err> fs: file write error (-5)
[00:00:01.553,000] <err> main: Couldn't write to log file: -5
[00:00:01.553,000] <err> main: Couldn't finish write: -5
[00:00:01.554,000] <err> fs: file write error (-9)
[00:00:01.554,000] <err> main: Couldn't write to log file: -9
[00:00:01.554,000] <err> main: Couldn't finish write: -9
[ The previous 3 lines repeat indefinitely ]

Environment (please complete the following information):

Additional context I've tested a few different cards and they have the same problems.

I've tried different clocks and bus widths.

I've tried to disable the internal pullups since there are already pullups on the breakout board. I added the following to my DTS overlay:

&sdmmc1_d0_pc8 {
    /delete-property/ bias-pull-up;
};
&sdmmc1_ck_pc12 {
    /delete-property/ bias-pull-up;
};
&sdmmc1_cmd_pd2 {
    /delete-property/ bias-pull-up;
};

I've looked at the signals with my oscilloscope and with the STM32U5, I see things happening on the CLK and CMD lines but nothing happening on the data lines. I haven't investigated the STM32H7 as deeply because it's not the MCU I'm interested in, just one I happened to have on hand.

I haven't tried eMMC modules (none on hand).

I'd love to test with a board that has a MicroSD slot already available but ST doesn't have any such STM32U5 boards.

github-actions[bot] commented 2 weeks ago

Hi @bobobo1618! We appreciate you submitting your first issue for our open-source project. 🌟

Even though I'm a bot, I can assure you that the whole community is genuinely grateful for your time and effort. 🤖💙

erwango commented 2 weeks ago

I've just had a try on the commit you mentioned on stm32h747i_disco with the following samples/subsys/fs/littlefs, and it works as expected:

*** Booting Zephyr OS build v3.7.0-1440-g34206f5505b5 ***                       
Sample program to r/w files on littlefs                                         
Area 0 at 0x0 on qspi-nor-flash-1@90000000 for 67108864 bytes                   
I: LittleFS version 2.9, disk version 2.1                                       
I: FS at qspi-nor-flash-1@90000000:0x0 is 8192 0x2000-byte blocks with 512 cycle
I: sizes: rd 32 ; pr 32 ; ca 256 ; la 64                                        
E: WEST_TOPDIR/modules/fs/littlefs/lfs.c:1374: Corrupted dir pair at {0x0, 0x1} 
W: can't mount (LFS -84); formatting                                            
I: /lfs1 mounted                                                                
/lfs1 mount: 0                                                                  
/lfs1: bsize = 32 ; frsize = 8192 ; blocks = 8192 ; bfree = 8190                

Listing dir /lfs1 ...                                                           
/lfs1/boot_count read count:0 (bytes: 0)                                        
/lfs1/boot_count write new boot count 1: [wr:1]                                 
I: Test file: /lfs1/pattern.bin not found, create one!                          
------ FILE: /lfs1/pattern.bin ------                                           
01 55 55 55 55 55 55 55 02 55 55 55 55 55 55 55                               

This should help as a base to investigate the issue you're facing, at least on STM32H7. I'll need some time to explore the status on STM32U5 (which seems to be a different issue, from the log you report).

bobobo1618 commented 2 weeks ago

Thanks for investigating! Your log indicates that you're using flash though, not a MicroSD: Area 0 at 0x0 on qspi-nor-flash-1@90000000 for 67108864 bytes. You need to do something like this to use the SD card: west build -b nucleo_h743zi samples/subsys/fs/littlefs -- -DCONF_FILE=prj_blk.conf.

However I was able to make this example as well as fs_sample work on both my boards (though the H743ZI needs the clock for sdmmc1 set to HSI48 still).

I'll close this for now and reopen if I'm able to write some code to cleanly reproduce the problem.

bobobo1618 commented 2 weeks ago

I narrowed down the problem to CONFIG_PM=y. When this is present, SDMMC is not functional.

I have a repro branch here where I've modified samples/fs/fs_sample to reproduce the issue.

Output:

*** Booting Zephyr OS build v3.7.0-1626-gdcec6d3cfb8f ***
[00:00:00.015,000] <inf> main: Block count 123596800
Sector size 512
Memory Size(MB) 60350
[00:00:02.024,000] <err> stm32_sdmmc: sd read error 32
[00:00:02.030,000] <err> fs: fs mount error (-5)
Error mounting disk.
[00:00:02.038,000] <err> fs: fs not mounted (mp == 0x20000008)
Finished.
erwango commented 2 weeks ago

Your log indicates that you're using flash though, not a MicroSD:

Oops, my bad. I've forgot there were 2 options to make it work.

I narrowed down the problem to CONFIG_PM=y. When this is present, SDMMC is not functional.

Good, let's investigate that. However, I'll move the issue to Enhancements as nothing has been done for PM support on this driver.

bobobo1618 commented 2 weeks ago

Do I understand correctly that getting this to work with PM would mostly consist of adding device_busy_set and device_busy_clear calls to the relevant places in the stm32 sdmmc driver? Or is there likely to be more to it than that?

If it's relatively straightforward, I can take a stab at it.

bobobo1618 commented 2 weeks ago

pm_policy_state_lock_get(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES); and pm_policy_state_lock_put(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES); to disable/re-enable the stop modes when I need the SDMMC peripheral seems to be enough to get things working reliably for my application, in case anyone runs into this and wants a quick fix.