From patchwork Mon Feb 2 08:33:46 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Szyprowski X-Patchwork-Id: 5759971 Return-Path: X-Original-To: patchwork-linux-samsung-soc@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 13D46BF440 for ; Mon, 2 Feb 2015 08:34:21 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id EB495201F4 for ; Mon, 2 Feb 2015 08:34:19 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 3DF8120253 for ; Mon, 2 Feb 2015 08:34:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752940AbbBBIeQ (ORCPT ); Mon, 2 Feb 2015 03:34:16 -0500 Received: from mailout1.w1.samsung.com ([210.118.77.11]:35375 "EHLO mailout1.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753142AbbBBIeN (ORCPT ); Mon, 2 Feb 2015 03:34:13 -0500 Received: from eucpsbgm2.samsung.com (unknown [203.254.199.245]) by mailout1.w1.samsung.com (Oracle Communications Messaging Server 7u4-24.01(7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTP id <0NJ4007JJYNUOA60@mailout1.w1.samsung.com>; Mon, 02 Feb 2015 08:38:18 +0000 (GMT) X-AuditID: cbfec7f5-b7fc86d0000066b7-67-54cf35f0bbf6 Received: from eusync2.samsung.com ( [203.254.199.212]) by eucpsbgm2.samsung.com (EUCPMTA) with SMTP id D9.53.26295.0F53FC45; Mon, 02 Feb 2015 08:31:44 +0000 (GMT) Received: from amdc1339.digital.local ([106.116.147.30]) by eusync2.samsung.com (Oracle Communications Messaging Server 7u4-23.01 (7.0.4.23.0) 64bit (built Aug 10 2011)) with ESMTPA id <0NJ400L97YGRC600@eusync2.samsung.com>; Mon, 02 Feb 2015 08:34:10 +0000 (GMT) From: Marek Szyprowski To: linux-mmc@vger.kernel.org, linux-samsung-soc@vger.kernel.org Cc: Marek Szyprowski , Kukjin Kim , Tobias Jakobi , Daniel Drake , Sebastian Reichel , Seungwon Jeon , Jaehoon Chung , Ulf Hansson , Chris Ball , Joonyoung Shim , Javier Martinez Canillas Subject: [PATCH v2 1/3] mmc: pwrseq: add driver for emmc hardware reset Date: Mon, 02 Feb 2015 09:33:46 +0100 Message-id: <1422866028-27891-2-git-send-email-m.szyprowski@samsung.com> X-Mailer: git-send-email 1.9.2 In-reply-to: <1422866028-27891-1-git-send-email-m.szyprowski@samsung.com> References: <1422866028-27891-1-git-send-email-m.szyprowski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFprDLMWRmVeSWpSXmKPExsVy+t/xK7ofTM+HGEzrtLCYcHk7o8Wj+Y+Z La79nsFmceNXG6vFi3sXWSz6H79mtjjyv5/RYsb5fUwWXT9/slmsPXKX3eL07hKLD/cvMlsc XxvuwOvxd3Yrs8ei71keizftZ/PYtKqTzePOtT1sHjdeLWTy6NuyitHj8ya5AI4oLpuU1JzM stQifbsEroyeWRuYCy5YVEzuucDSwPhJr4uRg0NCwETiRKtgFyMnkCkmceHeejYQW0hgKaPE jFmaXYxcQHYfk0TPgrOMIAk2AUOJrrddbCC9IgIOEi/OO4HUMAtsZJb41H0ArEZYwF3i6MI2 VpAaFgFViedH4kBMXgEPiYaZChCr5CT+v1zBBGJzCnhKbDh9gwVirYfEuptNLBMYeRcwMqxi FE0tTS4oTkrPNdIrTswtLs1L10vOz93ECAnRrzsYlx6zOsQowMGoxMP7gPtciBBrYllxZe4h RgkOZiURXuGPQCHelMTKqtSi/Pii0pzU4kOMTBycUg2Mc89XcnHdyT3bduqb/ZTDxwzsWu/y 1+/ycdm+8xXLponfY4+rn/c+ViD91rm8MLNW+U3AG77GF1OCLnOssZu3bGOaT5yNZubpnsCJ aZVZn51NnhTJrlSbd36t66+AlYk1+avVnZjj1dNVDX5u3+W4rIlTKupavWrBye+mZ7YYJzAZ fLjw+t0LJZbijERDLeai4kQA/qdo4i8CAAA= Sender: linux-samsung-soc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-samsung-soc@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch provides a simple mmc-pwrseq-emmc driver, which controls single gpio line. It perform standard eMMC hw reset procedure, as descibed by Jedec 4.4 specification. This procedure is performed just after MMC core enabled power to the given mmc host (to fix possible issues if bootloader has left eMMC card in initialized or unknown state), and before performing complete system reboot (also in case of emergency reboot call). The latter is needed on boards, which doesn't have hardware reset logic connected to emmc card and (limited or broken) ROM bootloaders are unable to read second stage from the emmc card if the card is left in unknown or already initialized state. Signed-off-by: Marek Szyprowski --- .../devicetree/bindings/mmc/mmc-pwrseq-emmc.txt | 25 +++++ drivers/mmc/core/Makefile | 2 +- drivers/mmc/core/pwrseq.c | 3 + drivers/mmc/core/pwrseq.h | 1 + drivers/mmc/core/pwrseq_emmc.c | 108 +++++++++++++++++++++ 5 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/mmc/mmc-pwrseq-emmc.txt create mode 100644 drivers/mmc/core/pwrseq_emmc.c diff --git a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-emmc.txt b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-emmc.txt new file mode 100644 index 000000000000..4baeac828f6b --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-emmc.txt @@ -0,0 +1,25 @@ +* The simple eMMC hardware reset provider + +The purpose of this driver is to perform standard eMMC hw reset +procedure, as descibed by Jedec 4.4 specification. This procedure is +performed just after MMC core enabled power to the given mmc host (to +fix possible issues if bootloader has left eMMC card in initialized or +unknown state), and before performing complete system reboot (also in +case of emergency reboot call). The latter is needed on boards, which +doesn't have hardware reset logic connected to emmc card and (limited or +broken) ROM bootloaders are unable to read second stage from the emmc +card if the card is left in unknown or already initialized state. + +Required properties: +- compatible : contains "mmc-pwrseq-emmc". +- reset-gpios : contains a GPIO specifier. The reset GPIO is pulled + down and then pulled up to perform eMMC card reset. To perform + reset procedure as described in Jedec 4.4 specification, the + gpio line should be defined as GPIO_ACTIVE_LOW. + +Example: + + sdhci0_pwrseq { + compatible = "mmc-pwrseq-emmc"; + reset-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>; + } diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index b39cbd2f830b..2c25138f28b7 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile @@ -8,5 +8,5 @@ mmc_core-y := core.o bus.o host.o \ sdio.o sdio_ops.o sdio_bus.o \ sdio_cis.o sdio_io.o sdio_irq.o \ quirks.o slot-gpio.o -mmc_core-$(CONFIG_OF) += pwrseq.o pwrseq_simple.o +mmc_core-$(CONFIG_OF) += pwrseq.o pwrseq_simple.o pwrseq_emmc.o mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o diff --git a/drivers/mmc/core/pwrseq.c b/drivers/mmc/core/pwrseq.c index 2cea00ed4e65..862356123d78 100644 --- a/drivers/mmc/core/pwrseq.c +++ b/drivers/mmc/core/pwrseq.c @@ -26,6 +26,9 @@ static struct mmc_pwrseq_match pwrseq_match[] = { { .compatible = "mmc-pwrseq-simple", .alloc = mmc_pwrseq_simple_alloc, + }, { + .compatible = "mmc-pwrseq-emmc", + .alloc = mmc_pwrseq_emmc_alloc, }, }; diff --git a/drivers/mmc/core/pwrseq.h b/drivers/mmc/core/pwrseq.h index bd860d88f116..aba3409e8d6e 100644 --- a/drivers/mmc/core/pwrseq.h +++ b/drivers/mmc/core/pwrseq.h @@ -28,6 +28,7 @@ void mmc_pwrseq_power_off(struct mmc_host *host); void mmc_pwrseq_free(struct mmc_host *host); int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev); +int mmc_pwrseq_emmc_alloc(struct mmc_host *host, struct device *dev); #else diff --git a/drivers/mmc/core/pwrseq_emmc.c b/drivers/mmc/core/pwrseq_emmc.c new file mode 100644 index 000000000000..bedf969244fa --- /dev/null +++ b/drivers/mmc/core/pwrseq_emmc.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2015, Samsung Electronics Co., Ltd. + * + * Author: Marek Szyprowski + * + * License terms: GNU General Public License (GPL) version 2 + * + * Simple eMMC hardware reset trigger + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "pwrseq.h" + +struct mmc_pwrseq_emmc { + struct mmc_pwrseq pwrseq; + struct notifier_block reset_nb; + struct gpio_desc *reset_gpio; +}; + +static void mmc_pwrseq_perform_emmc_reset(struct mmc_pwrseq_emmc *pwrseq) +{ + gpiod_set_value(pwrseq->reset_gpio, 1); + /* For eMMC, minimum is 1us but give it 10us for good measure */ + udelay(10); + gpiod_set_value(pwrseq->reset_gpio, 0); + /* For eMMC, minimum is 200us but give it 300us for good measure */ + udelay(300); +} + +static void mmc_pwrseq_emmc_reset(struct mmc_host *host) +{ + struct mmc_pwrseq_emmc *pwrseq = container_of(host->pwrseq, + struct mmc_pwrseq_emmc, pwrseq); + + if (!IS_ERR(pwrseq->reset_gpio)) + mmc_pwrseq_perform_emmc_reset(pwrseq); +} + +static void mmc_pwrseq_emmc_free(struct mmc_host *host) +{ + struct mmc_pwrseq_emmc *pwrseq = container_of(host->pwrseq, + struct mmc_pwrseq_emmc, pwrseq); + + if (!IS_ERR(pwrseq->reset_gpio)) + gpiod_put(pwrseq->reset_gpio); + + unregister_restart_handler(&pwrseq->reset_nb); + kfree(pwrseq); + host->pwrseq = NULL; +} + +static struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = { + .post_power_on = mmc_pwrseq_emmc_reset, + .power_off = mmc_pwrseq_emmc_reset, + .free = mmc_pwrseq_emmc_free, +}; + +static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + struct mmc_pwrseq_emmc *pwrseq; + pwrseq = container_of(this, struct mmc_pwrseq_emmc, reset_nb); + if (!IS_ERR(pwrseq->reset_gpio)) + mmc_pwrseq_perform_emmc_reset(pwrseq); + return NOTIFY_DONE; +} + +int mmc_pwrseq_emmc_alloc(struct mmc_host *host, struct device *dev) +{ + struct mmc_pwrseq_emmc *pwrseq; + int ret = 0; + + pwrseq = kzalloc(sizeof(struct mmc_pwrseq_emmc), GFP_KERNEL); + if (!pwrseq) + return -ENOMEM; + + pwrseq->reset_gpio = gpiod_get_index(dev, "reset", 0, GPIOD_OUT_LOW); + if (IS_ERR(pwrseq->reset_gpio) && + PTR_ERR(pwrseq->reset_gpio) != -ENOENT && + PTR_ERR(pwrseq->reset_gpio) != -ENOSYS) { + ret = PTR_ERR(pwrseq->reset_gpio); + goto free; + } + + /* + * register reset handler to ensure emmc reset also from + * emergency_reboot() + */ + pwrseq->reset_nb.notifier_call = mmc_pwrseq_emmc_reset_nb; + pwrseq->reset_nb.priority = 255; + register_restart_handler(&pwrseq->reset_nb); + + pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops; + host->pwrseq = &pwrseq->pwrseq; + + return 0; +free: + kfree(pwrseq); + return ret; +}