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-15 00:25:26
Also in:
linux-arm-kernel, linux-mmc, linux-rockchip
在 2026/01/15 星期四 3:51, Marco Schirrmeister 写道:
Hello Shawn, On Wed, Jan 14, 2026 at 9:08 AM Shawn Lin [off-list ref] wrote:quoted
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@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.
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.
--- 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
--- 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(structplatform_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 = {