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.14k stars 6.23k forks source link

Add 4-bit SD Card support to ESP32-S3 #75535

Open EricNRS opened 1 week ago

EricNRS commented 1 week ago

The ESP32 SD/MMC Host Controller Driver (drivers/sdhc/sdhc_esp32.c) supports 1-bit to 4-bit SDIO, but currently only supports the ESP32. Is it possible easily extend this for the ESP32-S3 or is SD/MMC controller substantially different requiring more extensive changes?

The following configuration was tested with v3.7.0-rc2 added to the test the support and it exposed the lack of the low-level HAL layer for the ESP32-S3. The HAL layer looks to be relatively simple, but needs someone knowledgeable in this area to take a look:

/home/work/zephyrproject-3.7/zephyr/drivers/sdhc/sdhc_esp32.c:18:10: fatal error: hal/sdmmc_ll.h: No such file or directory
18 | #include <hal/sdmmc_ll.h>
/ {
    soc {
        sdhc: sdhc@60028000 {
            compatible = "espressif,esp32-sdhc";
            reg = <0x60028000 0x1000>;
            interrupts = <SDIO_HOST_INTR_SOURCE>;
            interrupt-parent = <&intc>;
            clocks = <&rtc ESP32_SDMMC_MODULE>;
            #address-cells = <1>;
            #size-cells = <0>;

            sdhc0: sdhc@0 {
                compatible = "espressif,esp32-sdhc-slot";
                reg = <0>;
                status = "okay";

                pinctrl-0 = <&sdhc0_default>;
                pinctrl-names = "default";
                power-delay-ms = <100>;
                max-bus-freq = <52000000>;
                bus-width = <4>;

                clk-pin = <8>;
                cmd-pin = <9>;
                d0-pin = <10>;
                d1-pin = <11>;
                d2-pin = <12>;

                mmc {
                    compatible = "zephyr,sdmmc-disk";
                    status = "okay";
                };              
            };

            sdhc1: sdhc@1 {
                compatible = "espressif,esp32-sdhc-slot";
                reg = <1>;
                status = "disabled";
            };
        };
    };
};

&pinctrl {

    sdhc0_default: sdhc0_default {
        group1 {
            pinmux = < ESP32_PINMUX(6, ESP_SDHOST_CARD_DETECT_N_1, ESP_NOSIG) >;
            bias-pull-up;
            output-high;
        };
    };
};

Describe the solution you'd like Add support for ESP32-S3 SD/MMC Host Controller

Describe alternatives you've considered As a backup, the 1-bit (SPI) driver built into Zephyr does work, but is substantially slower.

EricNRS commented 1 week ago

@sylvioalves FYI. Not urgent since SPI mode does work with the native Zephyr driver, but would be extremely useful to speed up file access.

raffarost commented 1 week ago

Hi @EricNRS we have the implementation below, which still couldn't be tested for lack of a proper board. Would you be willing to see if it works?

ZEP HAL

EricNRS commented 1 week ago

Thanks @raffarost. I gave the changes a try, but it is timing out detecting the SD card (see boot logs below). Putting an oscilloscope on the CLK line shows a 450ms low pulse, but no actual clocking pulses, so I suspect that the issue is in the clock source. No activity on other lines as well. I am using sdhc0 on my board, but also tried sdhc1 with the same results.

Any ideas on the best way to debug?

CPU frequency is 240 MHz and APB frequency is 80 MHz for my ESP32-S3 setup.

[00:00:00.717,000] <dbg> sd: sd_init_io: Host controller support 3.3V max
[00:00:00.717,000] <dbg> sd: sd_init_io: Resetting power to card
[00:00:00.717,000] <inf> sdhc: SDHC I/O: slot: 0, bus width 1, clock 0Hz, card power OFF, voltage 3.3V
[00:00:01.217,000] <inf> sdhc: SDHC I/O: slot: 0, bus width 1, clock 0Hz, card power ON, voltage 3.3V
[00:00:01.717,000] <inf> sdhc: SDHC I/O: slot: 0, bus width 1, clock 400000Hz, card power ON, voltage 3.3V
[00:00:01.919,000] <err> sdhc: sdmmc_handle_event: sdmmc_host_wait_for_event returned 0xfffffff5, timeout 200 ms
[00:00:01.919,000] <err> sd: Card error on CMD0
[00:00:01.919,000] <err> fs: fs mount error (-5)
[00:00:01.919,000] <err> sdcard: Failed to mount SD card: -5
&sdhc {
    sdhc0: sdhc@0 {
        status = "okay";

        pinctrl-0 = <&sdhc0_default>;
        pinctrl-names = "default";
        max-bus-freq = <52000000>;
        bus-width = <4>;

        mmc {
            compatible = "zephyr,sdmmc-disk";
            status = "okay";
        };
    };
};

&pinctrl {
    sdhc0_default: sdhc0_default {
        group1 {
            pinmux = <SDHC0_CLKOUT_GPIO8>,
                 <SDHC0_CMD_GPIO9>,
                 <SDHC0_DATA0_GPIO10>,
                 <SDHC0_DATA1_GPIO11>,
                 <SDHC0_DATA2_GPIO12>,
                 <SDHC0_DATA3_GPIO7>;
            bias-pull-up;
        };
    };
};
raffarost commented 1 week ago

@EricNRS thanks for your feedback. I checked with the debugger and the piece of code changed to enable clock should be okay. Other variable to check would be pinctrl, but I still didn't find any fault in the configuration. I got a working setup here, so I'll work on it when I can and let you know.

raffarost commented 1 week ago

Hi @EricNRS we have the implementation below, which still couldn't be tested for lack of a proper board. Would you be willing to see if it works?

ZEP HAL

@EricNRS implementation was updated with the fix needed. I could test it only in 1 bit mode however, as I'm lacking a proper 4 bit SD socket. Could you try again and let me know? If it works in 4 bit mode I'll open the PR for review. Thanks

EricNRS commented 1 week ago

@raffarost with the new changes, the SD card is correctly initializing and working in 4-bit mode!

Just as a side note, I did have some issues with it not working correctly and then working, but with extremely slow performance, but that is likely because the SD Card is not being reset since I do not have power-control support the version of hardware I am testing with. Removing the SD Card and reinserting it resolved the issue and I haven't seen any issues since.

Correctly initialized in 4-bit mode, 50 MHz. Signals look good on my hardware.

[00:00:00.744,000] <dbg> sd: sd_init_io: Host controller support 3.3V max
[00:00:00.746,000] <dbg> sd: sd_init_io: Resetting power to card
[00:00:00.748,000] <inf> sdhc: SDHC I/O: slot: 0, bus width 1, clock 0Hz, card power OFF, voltage 3.3V
[00:00:01.250,000] <inf> sdhc: SDHC I/O: slot: 0, bus width 1, clock 0Hz, card power ON, voltage 3.3V
[00:00:01.753,000] <inf> sdhc: SDHC I/O: slot: 0, bus width 1, clock 400000Hz, card power ON, voltage 3.3V
[00:00:01.757,000] <dbg> sd: sd_send_interface_condition: Found SDHC with CMD8 support
[00:00:01.793,000] <dbg> sd: sdmmc_send_ocr: SDMMC responded to ACMD41 after 3 attempts
[00:00:01.795,000] <dbg> sd: card_read_cid: Card MID: 0x3, OID: DS
[00:00:01.797,000] <dbg> sd: sdmmc_request_rca: Card relative addr: 43690
[00:00:01.800,000] <dbg> sd: sdmmc_read_csd: Card block count 62333952, block size 512
[00:00:01.802,000] <inf> sdhc: SDHC I/O: slot: 0, bus width 1, clock 25000000Hz, card power ON, voltage 3.3V
[00:00:01.805,000] <inf> sdhc: Bus clock successfully set to 25000 kHz
[00:00:01.809,000] <dbg> sd: sdmmc_read_scr: SD reports specification version 8
[00:00:01.814,000] <dbg> sd: sdmmc_set_bus_speed: Setting bus clock to: 50000000
[00:00:01.816,000] <inf> sdhc: SDHC I/O: slot: 0, bus width 1, clock 50000000Hz, card power ON, voltage 3.3V
[00:00:01.818,000] <inf> sdhc: Bus clock successfully set to 50000 kHz
[00:00:01.820,000] <inf> sdhc: Bus timing successfully changed to HS
[00:00:01.821,000] <inf> sdhc: SDHC I/O: slot: 0, bus width 4, clock 50000000Hz, card power ON, voltage 3.3V
[00:00:01.824,000] <inf> sdhc: Bus width set successfully to 4 bit
[00:00:01.826,000] <dbg> sd: card_read: READ: Sector = 0, Count = 1
[00:00:01.831,000] <dbg> sd: card_read: READ: Sector = 8192, Count = 1
[00:00:01.834,000] <dbg> sd: card_read: READ: Sector = 8193, Count = 1
[00:00:01.836,000] <dbg> fs: fs_mount: fs mounted at /SD:
[00:00:01.838,000] <inf> sdcard: SD card successfully mounted

Performance is approximately 30% better in 4-bit mode compared with single SPI mode:

uart> fs erase_write_test SD:/test.bin 131072 10
...
Loop #10 done in 298ms.
Total: 3174ms, Per loop: ~317ms, Speed: ~403.3KiBps

For comparison, using the zephyr,sdhc-spi-slot driver with SPI directly which ends up selecting 24 MHz.

[00:00:00.741,000] <dbg> sd: sd_init_io: Host controller support 3.3V max
[00:00:00.743,000] <dbg> sd: sd_init_io: Resetting power to card
[00:00:00.750,000] <dbg> sd: sd_send_interface_condition: Found SDHC with CMD8 support
[00:00:00.911,000] <dbg> sd: sdmmc_send_ocr: SDMMC responded to ACMD41 after 14 attempts
[00:00:00.915,000] <dbg> sd: card_read_cid: Card MID: 0x3, OID: DS
[00:00:00.918,000] <dbg> sd: sdmmc_read_csd: Card block count 62333952, block size 512
[00:00:00.920,000] <inf> sd: Maximum SD clock is under 25MHz, using clock of 24000000Hz
[00:00:00.925,000] <dbg> sd: sdmmc_read_scr: SD reports specification version 8
. . .

uart> fs erase_write_test SD:/test.bin 131072 10
...
Loop #10 done in 99ms.
Total: 1045ms, Per loop: ~104ms, Speed: ~306.2KiBps
EricNRS commented 1 week ago

Thank you for your work on this, I really appreciate it!

raffarost commented 6 days ago

hi @EricNRS, happy it worked! I didn't encounter poor performance states during testing, but it may happen for the card to get stuck in very specific situations like after a failed or interrupted command sequence. It shouldn't happen in normal use though, but you can add to your hardware design a power line (pwr-gpios) and let Zephyr API control it. It will power cycle the card during initialization.

I found a bit odd 4 bit being 30% faster than 1 bit SPI, so I might take some time to do some benchmark testing.

Thanks for your feedback. I'll soon open the PR for review so we can have it merged when possible.

EricNRS commented 6 days ago

@raffarost I was a bit surprised the performance was only 30% faster as well, but it could be an issue with the test or elsewhere in the call sequence. I am also using FatFS on this SD card, so there is a chance that this is the performance bottleneck. There also appears to be a busy loop somewhere in the call sequence since when I had slow performance issues, it triggered the watchdog as well, so somewhere the code is not yielding while waiting, but this may be a deliberate feature.