Thread (21 messages) 21 messages, 6 authors, 2026-01-16

Re: [PATCH v1 0/3] mmc: dw_mmc-rockchip: Add stability quirk for NanoPi R76S

From: Shawn Lin <shawn.lin@rock-chips.com>
Date: 2026-01-16 05:07:56
Also in: linux-devicetree, linux-mmc, linux-rockchip

在 2026/01/16 星期五 3:39, Marco Schirrmeister 写道:
Hello Shawn,

On Thu, Jan 15, 2026 at 1:25 AM Shawn Lin [off-list ref] wrote:
quoted
quoted
Based on this, it makes me believe that power to the sd card is completely cut
and when it wakes up and knows how to continue, it still must go through the
retraining phase.
This is another mistake for your NanoPi R76S board. Before sent this
patch, I already checked the dts and saw sdmmc uses vmmc-supply =
<&vcc_3v3_s3> which is marked as regulator-always-on, but it's *NOT*
actually per the shcematic[1][2]. So need another fix for your board
to make it actually gpio-based power controller instaed of function
IO based, as when powering off the power domain, the power control bit
will not be able to maintain.

[1]
https://wiki.friendlyelec.com/wiki/images/6/60/NanoPi_R76S_LP4X_2411_SCH.pdf
[2]
https://wiki.friendlyelec.com/wiki/images/9/90/NanoPi_R76S_LP5_2411_SCH.pdf

Except for the patches you have tested, please append the blow patch as
well to test.
Bingo. These DTS additions fixed the power stability for the SD card.
I have verified that the card now wakes up instantly with no errors and
no retraining delay.
Thanks, I will fold these two patches into my thread of V4 to fix all
these problems.
I tested this both with and without your driver patch (the save/restore
phases logic).

This confirms the issue was indeed the power rail being cut during
idle periods. Great find on the schematic!
quoted
--- a/arch/arm64/boot/dts/rockchip/rk3576-nanopi-r76s.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3576-nanopi-r76s.dts
@@ -192,6 +192,18 @@
                  regulator-name = "vcc_3v3_s0";
                  vin-supply = <&vcc_3v3_s3>;
          };
+
+       vcc3v3_sd: regulator-vcc-3v3-sd {
+               compatible = "regulator-fixed";
+               enable-active-high;
+               gpios = <&gpio0 RK_PB6 GPIO_ACTIVE_HIGH>;
+               pinctrl-names = "default";
+               pinctrl-0 = <&sdmmc_pwren>;
+               regulator-name = "vcc3v3_sd";
+               regulator-min-microvolt = <3300000>;
+               regulator-max-microvolt = <3300000>;
+               vin-supply = <&vcc_3v3_s0>;
+       };
   };

   &combphy0_ps {
@@ -726,6 +738,12 @@
                  };
          };

+       sdmmc {
+               sdmmc_pwren: sdmmc-pwren {
+                       rockchip,pins = <0 RK_PB6 RK_FUNC_GPIO
&pcfg_pull_none>;
+               };
+       };
+
          usb {
                  usb_otg0_pwren_h: usb-otg0-pwren-h {
                          rockchip,pins = <0 RK_PD1 RK_FUNC_GPIO
&pcfg_pull_none>;
@@ -751,11 +769,14 @@
          bus-width = <4>;
          cap-mmc-highspeed;
          cap-sd-highspeed;
+       cd-gpios = <&gpio0 RK_PA7 GPIO_ACTIVE_LOW>;
          disable-wp;
          no-mmc;
          no-sdio;
+       pinctrl-names = "default";
+       pinctrl-0 = <&sdmmc0_clk &sdmmc0_cmd &sdmmc0_det &sdmmc0_bus4>;
          sd-uhs-sdr104;
-       vmmc-supply = <&vcc_3v3_s3>;
+       vmmc-supply = <&vcc3v3_sd>;
          vqmmc-supply = <&vccio_sd_s0>;
          status = "okay";



quoted
quoted
--- a/drivers/mmc/host/dw_mmc-rockchip.c
+++ b/drivers/mmc/host/dw_mmc-rockchip.c
@@ -36,6 +36,8 @@ struct dw_mci_rockchip_priv_data {
           int                     default_sample_phase;
           int                     num_phases;
           bool                    internal_phase;
+       int                     sample_phase;
+       int                     drv_phase;
    };

    /*
@@ -573,9 +575,43 @@ static void dw_mci_rockchip_remove(struct
platform_device *pdev)
           dw_mci_pltfm_remove(pdev);
    }

+static int dw_mci_rockchip_runtime_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct dw_mci *host = platform_get_drvdata(pdev);
+       struct dw_mci_rockchip_priv_data *priv = host->priv;
+
+       if (priv->internal_phase) {
+               priv->sample_phase = rockchip_mmc_get_phase(host, true);
+               priv->drv_phase = rockchip_mmc_get_phase(host, false);
+       }
+
+       return dw_mci_runtime_suspend(dev);
+}
+
+static int dw_mci_rockchip_runtime_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct dw_mci *host = platform_get_drvdata(pdev);
+       struct dw_mci_rockchip_priv_data *priv = host->priv;
+       int ret;
+
+       ret = dw_mci_runtime_resume(dev);
+       if (ret)
+               return ret;
+
+       if (priv->internal_phase) {
+               rockchip_mmc_set_phase(host, true, priv->sample_phase);
+               rockchip_mmc_set_phase(host, false, priv->drv_phase);
+               mci_writel(host, MISC_CON, MEM_CLK_AUTOGATE_ENABLE);
+       }
+
+       return ret;
+}
+
    static const struct dev_pm_ops dw_mci_rockchip_dev_pm_ops = {
           SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
-       RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_runtime_resume, NULL)
+       RUNTIME_PM_OPS(dw_mci_rockchip_runtime_suspend,
dw_mci_rockchip_runtime_resume, NULL)
    };

    static struct platform_driver dw_mci_rockchip_pltfm_driver = {
  
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help