[07/12,SRU,B,C,D,OEM-B] mmc: rtsx_usb_sdmmc: Re-work card detection/removal support

Message ID 20190111083019.32135-8-acelan.kao@canonical.com
State New
Headers show
Series
  • To reduce the Realtek USB cardreader power consumption
Related show

Commit Message

AceLan Kao Jan. 11, 2019, 8:30 a.m.
From: Ulf Hansson <ulf.hansson@linaro.org>

BugLink: https://bugs.launchpad.net/bugs/1811337

The rtsx USB parent device, has logic to detect when a card is inserted
into the card slot. Although, the logic can't detect when a card is
removed. This makes things a bit tricky, which is why the current method is
simply to turn on MMC_CAP_NEEDS_POLL during probe.

Using MMC_CAP_NEEDS_POLL means lots of energy being wasted, as the mmc host
becomes runtime resumed frequently by the mmc core, while it polls for new
cards being inserted.

To address this problem, let's start relying on that the rtsx USB driver
runtime resumes its child device, which is the rtsx_usb_sdmmc device, when
it detects that a new card being inserted.

This means dropping MMC_CAP_NEEDS_POLL from being set during probe. Instead
let's implement a ->runtime_resume() callback to schedule a detect work and
to set MMC_CAP_NEEDS_POLL. In this way, polling is enabled as long as there
is card inserted, thus we can rely on the mmc core to detect also when the
card becomes removed.

Furthermore, to avoid polling forever after a card has been removed, let's
implement a ->runtime_suspend() callback and make it clear
MMC_CAP_NEEDS_POLL.

Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Tested-by: Kai-Heng Feng <kai.heng.feng@canonical.com>
Tested-by: Oleksandr Natalenko <oleksandr@natalenko.name>
(cherry picked from commit 4dad599b8b5d1ffc5ef12a2edb13d15d537202ba)
Signed-off-by: AceLan Kao <acelan.kao@canonical.com>
---
 drivers/mmc/host/rtsx_usb_sdmmc.c | 29 ++++++++++++++++++++++++++++-
 1 file changed, 28 insertions(+), 1 deletion(-)

Patch

diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c
index 5ba66396a8ae..7e7831c66e7c 100644
--- a/drivers/mmc/host/rtsx_usb_sdmmc.c
+++ b/drivers/mmc/host/rtsx_usb_sdmmc.c
@@ -28,6 +28,7 @@ 
 #include <linux/mmc/sd.h>
 #include <linux/mmc/card.h>
 #include <linux/scatterlist.h>
+#include <linux/pm.h>
 #include <linux/pm_runtime.h>
 
 #include <linux/rtsx_usb.h>
@@ -1324,7 +1325,7 @@  static void rtsx_usb_init_host(struct rtsx_usb_sdmmc *host)
 	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
 		MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
 		MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 |
-		MMC_CAP_NEEDS_POLL | MMC_CAP_ERASE | MMC_CAP_SYNC_RUNTIME_PM;
+		MMC_CAP_ERASE | MMC_CAP_SYNC_RUNTIME_PM;
 	mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE |
 		MMC_CAP2_NO_SDIO;
 
@@ -1429,6 +1430,31 @@  static int rtsx_usb_sdmmc_drv_remove(struct platform_device *pdev)
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int rtsx_usb_sdmmc_runtime_suspend(struct device *dev)
+{
+	struct rtsx_usb_sdmmc *host = dev_get_drvdata(dev);
+
+	host->mmc->caps &= ~MMC_CAP_NEEDS_POLL;
+	return 0;
+}
+
+static int rtsx_usb_sdmmc_runtime_resume(struct device *dev)
+{
+	struct rtsx_usb_sdmmc *host = dev_get_drvdata(dev);
+
+	host->mmc->caps |= MMC_CAP_NEEDS_POLL;
+	if (sdmmc_get_cd(host->mmc) == 1)
+		mmc_detect_change(host->mmc, 0);
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops rtsx_usb_sdmmc_dev_pm_ops = {
+	SET_RUNTIME_PM_OPS(rtsx_usb_sdmmc_runtime_suspend,
+			   rtsx_usb_sdmmc_runtime_resume, NULL)
+};
+
 static const struct platform_device_id rtsx_usb_sdmmc_ids[] = {
 	{
 		.name = "rtsx_usb_sdmmc",
@@ -1444,6 +1470,7 @@  static struct platform_driver rtsx_usb_sdmmc_driver = {
 	.id_table       = rtsx_usb_sdmmc_ids,
 	.driver		= {
 		.name	= "rtsx_usb_sdmmc",
+		.pm	= &rtsx_usb_sdmmc_dev_pm_ops,
 	},
 };
 module_platform_driver(rtsx_usb_sdmmc_driver);