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

Marco Schirrmeister mschirrmeister at gmail.com
Wed Jan 14 11:51:02 PST 2026


Hello Shawn,

On Wed, Jan 14, 2026 at 9:08 AM Shawn Lin <shawn.lin at rock-chips.com> wrote:
>
> Except for the patch mentioned above for fixing the hot-plug problem
> which you confirmed to work fine. I looked the code a bit and see
> a potential problem related to the runtime suspend + power-domain.
> Please check the patch to see if it fixes your problem:

I tested your patch and the issue is still present. For verification I did
add some debug statements and just to see what it saves and restores.

The symbols are there.
root at nanopi-r76s ~# grep dw_mci_rockchip_runtime /proc/kallsyms
ffff800080bf27a0 t dw_mci_rockchip_runtime_suspend
ffff800080bf29c0 t dw_mci_rockchip_runtime_resume

# dmesg output
[Wed Jan 14 20:13:46 2026] E220d: Restoring phases: sample=231, drv=180
[Wed Jan 14 20:13:46 2026] mmc_host mmc1: Bus speed (slot 0) = 400000Hz
[Wed Jan 14 20:13:47 2026] mmc_host mmc1: Bus speed (slot 0) = 198000000Hz
[Wed Jan 14 20:13:47 2026] dwmmc_rockchip 2a310000.mmc: Successfully
tuned phase to 232
[Wed Jan 14 20:13:48 2026] E220d: Saving phases: sample=231, drv=180
[Wed Jan 14 20:13:48 2026] E220d: Restoring phases: sample=231, drv=180
[Wed Jan 14 20:13:48 2026] mmc_host mmc1: Bus speed (slot 0) = 400000Hz
[Wed Jan 14 20:13:48 2026] mmc_host mmc1: Bus speed (slot 0) = 198000000Hz
[Wed Jan 14 20:13:48 2026] dwmmc_rockchip 2a310000.mmc: Successfully
tuned phase to 231
[Wed Jan 14 20:13:48 2026] E220d: Saving phases: sample=231, drv=180

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.

> --- 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