[PATCH v1 0/3] mmc: dw_mmc-rockchip: Add stability quirk for NanoPi R76S
Marco Schirrmeister
mschirrmeister at gmail.com
Thu Jan 15 11:39:34 PST 2026
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.
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