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

Shawn Lin shawn.lin at rock-chips.com
Thu Jan 15 16:31:44 PST 2026


在 2026/01/16 星期五 3:39, Marco Schirrmeister 写道:
> Hello Shawn,
> 
> On Thu, Jan 15, 2026 at 1:25 AM Shawn Lin <shawn.lin at rock-chips.com> wrote:
>>>
>>> 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!
> 
>> --- 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";
>>
>>
>>
>>
>>>> --- 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 = {
>>>
>>
> 




More information about the linux-arm-kernel mailing list