Thread (15 messages) 15 messages, 3 authors, 2014-06-30

Re: [RFC PATCH v2 6/6] mmc: core: add manual resume capability

From: Vincent Yang <hidden>
Date: 2014-06-27 11:00:21
Also in: linux-mmc, linuxppc-dev

2014-06-27 17:40 GMT+08:00 Ulf Hansson [off-list ref]:

On 27 juni 2014 04:23:42 CEST, Vincent Yang [off-list ref] wrote:
quoted
2014-06-26 17:42 GMT+08:00 Ulf Hansson [off-list ref]:
quoted

On 26 juni 2014 08:23:32 CEST, Vincent Yang
[off-list ref] wrote:
quoted
quoted
This patch adds manual resume for some embedded platforms with rootfs
stored in SD card. It references CONFIG_MMC_BLOCK_DEFERRED_RESUME in
kernel 3.10. It lets host controller driver to manually handle resume
by itself.

[symptom]
This issue is found on mb86s7x platforms with rootfs stored in SD
card.
quoted
quoted
It failed to resume form STR suspend mode because SD card cannot be
ready
in time. It take longer time (e.g., 600ms) to be ready for access.
The error log looks like below:

root@localhost:~# echo mem > /sys/power/state
[   30.441974] SUSPEND

SCB Firmware : Category 01 Version 02.03 Rev. 00_
   Config   : (no configuration)
root@localhost:~# [   30.702976] Buffer I/O error on device
mmcblk1p2,
quoted
quoted
logical block 31349
[   30.709678] Buffer I/O error on device mmcblk1p2, logical block
168073
[   30.716220] Buffer I/O error on device mmcblk1p2, logical block
168074
[   30.722759] Buffer I/O error on device mmcblk1p2, logical block
168075
[   30.729456] Buffer I/O error on device mmcblk1p2, logical block
31349
[   30.735916] Buffer I/O error on device mmcblk1p2, logical block
31350
[   30.742370] Buffer I/O error on device mmcblk1p2, logical block
31351
[   30.749025] Buffer I/O error on device mmcblk1p2, logical block
168075
[   30.755657] Buffer I/O error on device mmcblk1p2, logical block
31351
[   30.763130] Aborting journal on device mmcblk1p2-8.
[   30.768060] JBD2: Error -5 detected when updating journal
superblock
quoted
quoted
for mmcblk1p2-8.
[   30.776085] EXT4-fs error (device mmcblk1p2):
ext4_journal_check_start:56: Detected aborted journal
[   30.785259] EXT4-fs (mmcblk1p2): Remounting filesystem read-only
[   31.370716] EXT4-fs error (device mmcblk1p2):
ext4_find_entry:1309:
quoted
quoted
inode #2490369: comm udevd: reading directory lblock 0
[   31.382485] EXT4-fs error (device mmcblk1p2):
ext4_find_entry:1309:
quoted
quoted
inode #1048577: comm udevd: reading directory lblock 0

[analysis]
In system resume path, mmc_sd_resume() is failed with error code -123
because at that time SD card is still not ready on mb86s7x platforms.
So why does it fail? It shouldn't!

I get the impression that you are solving this in the wrong way.
Hi Uffe,
On mb86s7x EVB, power for SD card is completely removed when entering
STR suspend mode (i.e., echo mem > /sys/power/state). When system
starts to resume, it turns on the power for SD card again. However, It
take
longer time (e.g., 600ms) to get the power ready.
This is why mmc_sd_resume() failed with error code -123. At that time
point,
power for SD card is not ready yet.

At first we fixed it by a simple method of using
SDHCI_QUIRK_DELAY_AFTER_POWER:
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 169e17d..ed28896 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1283,7 +1283,7 @@ static void sdhci_set_power(struct sdhci_host
*host, unsigned char mode,
                * they can apply clock after applying power
                */
               if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER)
-                       mdelay(10);
+                       mdelay(600);
       }
If you can't model the power to the card through a regulator, this is what you need to do.
Hi Uffe,
Yes, I got it.
quoted
       if (host->vmmc) {

However, we found it blocks the system resume path. It can slow down
system resume and also booting.
Therefore, we would like to resume SD card manually and asynchronously
by host controller driver itself. Thus system resume path is not
blocked.
That is accomplished by using MMC_CAP_RUNTIME_RESUME.
Yes, I got it.
Thanks a lot for your review and help.
I will update it in next version.


Best regards,
Vincent Yang
Kind regards
Uffe

quoted
Thanks a lot for your review!


Best regards,
Vincent Yang
quoted
Kind regards
Uffe
quoted
[solution]
In order to not blocking system resume path, this patch just sets a
flag
MMC_BUSRESUME_MANUAL_RESUME when this error happened, and then host
controller
driver can understand it by this flag. Then host controller driver
have
quoted
quoted
to
resume SD card manually and asynchronously.

Signed-off-by: Vincent Yang <redacted>
---
drivers/mmc/core/core.c          |  4 ++
drivers/mmc/core/sd.c            |  4 ++
drivers/mmc/host/sdhci_f_sdh30.c | 89
++++++++++++++++++++++++++++++++++++++++
include/linux/mmc/host.h         | 14 +++++++
4 files changed, 111 insertions(+)
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 764af63..51fce49 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -2648,6 +2648,10 @@ int mmc_pm_notify(struct notifier_block
*notify_block,
      case PM_POST_RESTORE:

              spin_lock_irqsave(&host->lock, flags);
+              if (mmc_bus_manual_resume(host)) {
+                      spin_unlock_irqrestore(&host->lock, flags);
+                      break;
+              }
              host->rescan_disable = 0;
              spin_unlock_irqrestore(&host->lock, flags);
              _mmc_detect_change(host, 0, false);
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 0c44510..859390d 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -1133,6 +1133,10 @@ static int mmc_sd_resume(struct mmc_host
*host)
quoted
quoted
      if (!(host->caps & MMC_CAP_RUNTIME_RESUME)) {
              err = _mmc_sd_resume(host);
+              if ((host->caps2 & MMC_CAP2_MANUAL_RESUME) && err)
+                      mmc_set_bus_resume_policy(host, 1);
+              else
+                      mmc_set_bus_resume_policy(host, 0);
              pm_runtime_set_active(&host->card->dev);
              pm_runtime_mark_last_busy(&host->card->dev);
      }
diff --git a/drivers/mmc/host/sdhci_f_sdh30.c
b/drivers/mmc/host/sdhci_f_sdh30.c
index 6fae509..67bcff2 100644
--- a/drivers/mmc/host/sdhci_f_sdh30.c
+++ b/drivers/mmc/host/sdhci_f_sdh30.c
@@ -30,6 +30,12 @@
#include "../core/core.h"

#define DRIVER_NAME "f_sdh30"
+#define RESUME_WAIT_COUNT     100
+#define RESUME_WAIT_TIME      50
+#define RESUME_WAIT_JIFFIES   msecs_to_jiffies(RESUME_DETECT_TIME)
+#define RESUME_DETECT_COUNT   16
+#define RESUME_DETECT_TIME    50
+#define RESUME_DETECT_JIFFIES msecs_to_jiffies(RESUME_DETECT_TIME)


struct f_sdhost_priv {
@@ -38,8 +44,59 @@ struct f_sdhost_priv {
      int gpio_select_1v8;
      u32 vendor_hs200;
      struct device *dev;
+      unsigned int quirks;    /* Deviations from spec. */
+
+/* retry to detect mmc device when resume */
+#define F_SDH30_QUIRK_RESUME_DETECT_RETRY             (1<<0)
+
+      struct workqueue_struct *resume_detect_wq;
+      struct delayed_work resume_detect_work;
+      unsigned int            resume_detect_count;
+      unsigned int            resume_wait_count;
};

+static void sdhci_f_sdh30_resume_detect_work_func(struct work_struct
*work)
+{
+      struct f_sdhost_priv *priv = container_of(work, struct
f_sdhost_priv,
quoted
quoted
+              resume_detect_work.work);
+      struct sdhci_host *host = dev_get_drvdata(priv->dev);
+      int err = 0;
+
+      if (mmc_bus_manual_resume(host->mmc)) {
+              pm_runtime_disable(&host->mmc->card->dev);
+              mmc_card_set_suspended(host->mmc->card);
+              err = host->mmc->bus_ops->resume(host->mmc);
+              if (priv->resume_detect_count-- && err)
+                      queue_delayed_work(priv->resume_detect_wq,
+                                         &priv->resume_detect_work,
+                              RESUME_DETECT_JIFFIES);
+              else
+                      pr_debug("%s: resume detection done (count:%d,
wait:%d, err:%d)\n",
quoted
quoted
+                               mmc_hostname(host->mmc),
+                               priv->resume_detect_count,
+                               priv->resume_wait_count, err);
+      } else {
+              if (priv->resume_wait_count--)
+                      queue_delayed_work(priv->resume_detect_wq,
+                                         &priv->resume_detect_work,
+                              RESUME_WAIT_JIFFIES);
+              else
+                      pr_debug("%s: resume done\n",
mmc_hostname(host->mmc));
quoted
quoted
+      }
+}
+
+static void sdhci_f_sdh30_resume_detect(struct mmc_host *mmc,
+                                      int detect, int wait)
+{
+      struct sdhci_host *host = mmc_priv(mmc);
+      struct f_sdhost_priv *priv = sdhci_priv(host);
+
+      priv->resume_detect_count = detect;
+      priv->resume_wait_count = wait;
+      queue_delayed_work(priv->resume_detect_wq,
+                         &priv->resume_detect_work, 0);
+}
+
void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host)
{
      struct f_sdhost_priv *priv = sdhci_priv(host);
@@ -146,6 +203,12 @@ static int sdhci_f_sdh30_probe(struct
platform_device *pdev)
              }
      }

+      if (of_find_property(pdev->dev.of_node, "resume-detect-retry",
NULL))
quoted
quoted
{
+              dev_info(dev, "Applying resume detect retry quirk\n");
+              priv->quirks |= F_SDH30_QUIRK_RESUME_DETECT_RETRY;
+              host->mmc->caps2 |= MMC_CAP2_MANUAL_RESUME;
+      }
+
      ret = mmc_of_parse_voltage(pdev->dev.of_node,
&host->ocr_mask);
quoted
quoted
      if (ret) {
              dev_err(dev, "%s: parse voltage error\n", __func__);
@@ -197,6 +260,18 @@ static int sdhci_f_sdh30_probe(struct
platform_device *pdev)
              }
      }

+      /* Init workqueue */
+      if (priv->quirks & F_SDH30_QUIRK_RESUME_DETECT_RETRY) {
+              priv->resume_detect_wq =
create_workqueue("sdhci_f_sdh30");
quoted
quoted
+              if (priv->resume_detect_wq == NULL) {
+                      ret = -ENOMEM;
+                      dev_err(dev, "Failed to create resume
detection workqueue\n");
quoted
quoted
+                      goto err_init_wq;
+              }
+              INIT_DELAYED_WORK(&priv->resume_detect_work,
+
sdhci_f_sdh30_resume_detect_work_func);
quoted
quoted
+      }
+
      ret = sdhci_add_host(host);
      if (ret) {
              dev_err(dev, "%s: host add error\n", __func__);
@@ -229,6 +304,9 @@ static int sdhci_f_sdh30_probe(struct
platform_device *pdev)
      return 0;

err_add_host:
+      if (priv->quirks & F_SDH30_QUIRK_RESUME_DETECT_RETRY)
+              destroy_workqueue(priv->resume_detect_wq);
+err_init_wq:
      if (gpio_is_valid(priv->gpio_select_1v8)) {
              gpio_direction_output(priv->gpio_select_1v8, 1);
              gpio_free(priv->gpio_select_1v8);
@@ -268,6 +346,9 @@ static int sdhci_f_sdh30_remove(struct
platform_device *pdev)
              gpio_free(priv->gpio_select_1v8);
      }

+      if (priv->quirks & F_SDH30_QUIRK_RESUME_DETECT_RETRY)
+              destroy_workqueue(priv->resume_detect_wq);
+
      sdhci_free_host(host);
      platform_set_drvdata(pdev, NULL);
@@ -285,7 +366,15 @@ static int sdhci_f_sdh30_suspend(struct device
*dev)
static int sdhci_f_sdh30_resume(struct device *dev)
{
      struct sdhci_host *host = dev_get_drvdata(dev);
+      struct f_sdhost_priv *priv = sdhci_priv(host);

+      if (priv->quirks & F_SDH30_QUIRK_RESUME_DETECT_RETRY) {
+              pr_debug("%s: start resume detect\n",
+                       mmc_hostname(host->mmc));
+              sdhci_f_sdh30_resume_detect(host->mmc,
+                                          RESUME_DETECT_COUNT,
+                                          RESUME_WAIT_COUNT);
+      }
      return sdhci_resume_host(host);
}
#endif
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 7960424..55221dd 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -283,6 +283,7 @@ struct mmc_host {
#define MMC_CAP2_HS400                (MMC_CAP2_HS400_1_8V | \
                               MMC_CAP2_HS400_1_2V)
#define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 17)
+#define MMC_CAP2_MANUAL_RESUME     (1 << 18)  /* Resume manually
when
quoted
quoted
error */

      mmc_pm_flag_t           pm_caps;        /* supported pm
features */
quoted
quoted
@@ -338,6 +339,9 @@ struct mmc_host {
      const struct mmc_bus_ops *bus_ops;      /* current bus driver
*/
quoted
quoted
      unsigned int            bus_refs;       /* reference counter
*/
quoted
quoted
+      unsigned int            bus_resume_flags;
+#define MMC_BUSRESUME_MANUAL_RESUME   (1 << 0)
+
      unsigned int            sdio_irqs;
      struct task_struct      *sdio_irq_thread;
      bool                    sdio_irq_pending;
@@ -384,6 +388,16 @@ static inline void *mmc_priv(struct mmc_host
*host)
#define mmc_dev(x)    ((x)->parent)
#define mmc_classdev(x)       (&(x)->class_dev)
#define mmc_hostname(x)       (dev_name(&(x)->class_dev))
+#define mmc_bus_manual_resume(host) ((host)->bus_resume_flags &
        \
quoted
quoted
+      MMC_BUSRESUME_MANUAL_RESUME)
+
+static inline void mmc_set_bus_resume_policy(struct mmc_host *host,
int manual)
+{
+      if (manual)
+              host->bus_resume_flags |= MMC_BUSRESUME_MANUAL_RESUME;
+      else
+              host->bus_resume_flags &=
~MMC_BUSRESUME_MANUAL_RESUME;
quoted
quoted
+}

int mmc_power_save_host(struct mmc_host *host);
int mmc_power_restore_host(struct mmc_host *host);
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help