[RFC PATCH v2 6/6] mmc: core: add manual resume capability
Ulf Hansson
ulf.hansson at linaro.org
Thu Jun 26 19:42:44 EST 2014
On 26 juni 2014 08:23:32 CEST, Vincent Yang <vincent.yang.fujitsu at gmail.com> wrote:
>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.
>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 at localhost:~# echo mem > /sys/power/state
>[ 30.441974] SUSPEND
>
>SCB Firmware : Category 01 Version 02.03 Rev. 00_
> Config : (no configuration)
>root at localhost:~# [ 30.702976] Buffer I/O error on device mmcblk1p2,
>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
>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:
>inode #2490369: comm udevd: reading directory lblock 0
>[ 31.382485] EXT4-fs error (device mmcblk1p2): ext4_find_entry:1309:
>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.
Kind regards
Uffe
>
>[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
>to
>resume SD card manually and asynchronously.
>
>Signed-off-by: Vincent Yang <Vincent.Yang at tw.fujitsu.com>
>---
> 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)
>
> 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,
>+ 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",
>+ 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));
>+ }
>+}
>+
>+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))
>{
>+ 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);
> 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");
>+ if (priv->resume_detect_wq == NULL) {
>+ ret = -ENOMEM;
>+ dev_err(dev, "Failed to create resume detection workqueue\n");
>+ goto err_init_wq;
>+ }
>+ INIT_DELAYED_WORK(&priv->resume_detect_work,
>+ sdhci_f_sdh30_resume_detect_work_func);
>+ }
>+
> 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
>error */
>
> mmc_pm_flag_t pm_caps; /* supported pm features */
>
>@@ -338,6 +339,9 @@ struct mmc_host {
> const struct mmc_bus_ops *bus_ops; /* current bus driver */
> unsigned int bus_refs; /* reference counter */
>
>+ 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 & \
>+ 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;
>+}
>
> int mmc_power_save_host(struct mmc_host *host);
> int mmc_power_restore_host(struct mmc_host *host);
More information about the Linuxppc-dev
mailing list