From patchwork Sun Jul 13 06:30:25 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mollie Wu X-Patchwork-Id: 33540 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-vc0-f199.google.com (mail-vc0-f199.google.com [209.85.220.199]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 5D00020CB3 for ; Sun, 13 Jul 2014 06:30:45 +0000 (UTC) Received: by mail-vc0-f199.google.com with SMTP id ij19sf11296274vcb.10 for ; Sat, 12 Jul 2014 23:30:44 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:delivered-to:from:to:cc:subject :date:message-id:in-reply-to:references:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :list-post:list-help:list-archive:list-unsubscribe; bh=b2INc45aRyhliXehpR5RXt40eGBNxEljNKxN8MAZM5I=; b=nLKGzqXEBXlmo3jehknyO2G5zI3hion+MtoiDDm94RMF4K7FKUM0BV5bpHHm4prRJw iIxuXYqpIwUpJLN6AWZ9Zo9GJNKfo6rK/qT4lx+ncFV1uMqaI1FcUqobp8f2Eozb7wx1 RRL0/38Oe7ZRO7qnHmuP3FnbXCldKQagPeUYjNPalEtRLPVe8rf3IR+LSgnNvja0UGFi /TcogR6LRN32RjaZIN99PNT8KR47TMBSFHVwR3d4NxK+EKJBJOsdYN9r2P1BWr2Yqc0f SWqXHQh6zlegez2MHOj2K3vflAhIMubJkc9y/Pa/ZxIE59y1Zk733Jfm/9EZsxJgBmfS pmnQ== X-Gm-Message-State: ALoCoQkXdpforEPxjlWPww4xpr5mZUccQy3R97IW53+wKpjR8pZGscIDQ2o+z6vw3OHWkji2jwQL X-Received: by 10.236.124.131 with SMTP id x3mr3916811yhh.14.1405233044901; Sat, 12 Jul 2014 23:30:44 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.140.107.198 with SMTP id h64ls761281qgf.66.gmail; Sat, 12 Jul 2014 23:30:44 -0700 (PDT) X-Received: by 10.221.44.73 with SMTP id uf9mr9104322vcb.9.1405233044834; Sat, 12 Jul 2014 23:30:44 -0700 (PDT) Received: from mail-vc0-f178.google.com (mail-vc0-f178.google.com [209.85.220.178]) by mx.google.com with ESMTPS id c1si4200149vdw.37.2014.07.12.23.30.44 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Sat, 12 Jul 2014 23:30:44 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.220.178 as permitted sender) client-ip=209.85.220.178; Received: by mail-vc0-f178.google.com with SMTP id ij19so4993471vcb.37 for ; Sat, 12 Jul 2014 23:30:44 -0700 (PDT) X-Received: by 10.220.68.140 with SMTP id v12mr9065722vci.13.1405233044747; Sat, 12 Jul 2014 23:30:44 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patches@linaro.org Received: by 10.221.37.5 with SMTP id tc5csp45224vcb; Sat, 12 Jul 2014 23:30:44 -0700 (PDT) X-Received: by 10.68.186.97 with SMTP id fj1mr9104516pbc.37.1405233043887; Sat, 12 Jul 2014 23:30:43 -0700 (PDT) Received: from mail-pd0-f177.google.com (mail-pd0-f177.google.com [209.85.192.177]) by mx.google.com with ESMTPS id xz9si6505544pab.92.2014.07.12.23.30.43 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Sat, 12 Jul 2014 23:30:43 -0700 (PDT) Received-SPF: pass (google.com: domain of mollie.wu@linaro.org designates 209.85.192.177 as permitted sender) client-ip=209.85.192.177; Received: by mail-pd0-f177.google.com with SMTP id p10so192382pdj.8 for ; Sat, 12 Jul 2014 23:30:43 -0700 (PDT) X-Received: by 10.66.164.234 with SMTP id yt10mr9308034pab.65.1405233043531; Sat, 12 Jul 2014 23:30:43 -0700 (PDT) Received: from localhost.localdomain (123-192-197-11.dynamic.kbronet.com.tw. [123.192.197.11]) by mx.google.com with ESMTPSA id l3sm6965498pbq.8.2014.07.12.23.30.39 for (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Sat, 12 Jul 2014 23:30:43 -0700 (PDT) From: Mollie Wu To: linux-mmc@vger.kernel.org, linux-arm-kernel@lists.infradead.org Cc: andy.green@linaro.org, patches@linaro.org, jaswinder.singh@linaro.org, arnd@arndb.de, olof@lixom.net, linux@arm.linux.org.uk, chris@printf.net, anton@enomsg.org, Mollie Wu , Vincent Yang , Tetsuya Takinishi Subject: [PATCH 3/8] mmc: core: add manual resume capability Date: Sun, 13 Jul 2014 14:30:25 +0800 Message-Id: <1405233026-4649-1-git-send-email-mollie.wu@linaro.org> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: References: X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: mollie.wu@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.220.178 as permitted sender) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Precedence: list Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org List-ID: X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , 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@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, 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. [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 Cc: Arnd Bergmann Cc: Olof Cc: Russell King Signed-off-by: Tetsuya Takinishi Signed-off-by: Mollie Wu --- 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 8d23f2d..32864ba 100644 --- a/drivers/mmc/host/sdhci_f_sdh30.c +++ b/drivers/mmc/host/sdhci_f_sdh30.c @@ -29,6 +29,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) /* F_SDH30 extended Controller registers */ #define F_SDH30_AHB_CONFIG 0x100 @@ -61,8 +67,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); @@ -173,6 +230,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__); @@ -225,6 +288,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); + } + platform_set_drvdata(pdev, host); #ifdef CONFIG_PM_RUNTIME @@ -263,6 +338,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); @@ -302,6 +380,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); @@ -319,7 +400,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);