aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarek Szyprowski <m.szyprowski@samsung.com>2015-02-03 08:07:53 -0500
committerUlf Hansson <ulf.hansson@linaro.org>2015-02-04 03:45:09 -0500
commit726b6324e36b39788f9cdcb918df48bc4d475268 (patch)
tree65dd8aa7f2d32f9c428b2d68692e2835b37b9e44
parent3981c516664d81a3e83c1923fa1ab3988c529402 (diff)
mmc: pwrseq: add driver for emmc hardware reset
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 <m.szyprowski@samsung.com> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
-rw-r--r--Documentation/devicetree/bindings/mmc/mmc-pwrseq-emmc.txt25
-rw-r--r--drivers/mmc/core/Makefile2
-rw-r--r--drivers/mmc/core/pwrseq.c3
-rw-r--r--drivers/mmc/core/pwrseq.h1
-rw-r--r--drivers/mmc/core/pwrseq_emmc.c101
5 files changed, 131 insertions, 1 deletions
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..0cb827bf9435
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-emmc.txt
@@ -0,0 +1,25 @@
1* The simple eMMC hardware reset provider
2
3The purpose of this driver is to perform standard eMMC hw reset
4procedure, as descibed by Jedec 4.4 specification. This procedure is
5performed just after MMC core enabled power to the given mmc host (to
6fix possible issues if bootloader has left eMMC card in initialized or
7unknown state), and before performing complete system reboot (also in
8case of emergency reboot call). The latter is needed on boards, which
9doesn't have hardware reset logic connected to emmc card and (limited or
10broken) ROM bootloaders are unable to read second stage from the emmc
11card if the card is left in unknown or already initialized state.
12
13Required properties:
14- compatible : contains "mmc-pwrseq-emmc".
15- reset-gpios : contains a GPIO specifier. The reset GPIO is asserted
16 and then deasserted to perform eMMC card reset. To perform
17 reset procedure as described in Jedec 4.4 specification, the
18 gpio line should be defined as GPIO_ACTIVE_LOW.
19
20Example:
21
22 sdhci0_pwrseq {
23 compatible = "mmc-pwrseq-emmc";
24 reset-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>;
25 }
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 \
8 sdio.o sdio_ops.o sdio_bus.o \ 8 sdio.o sdio_ops.o sdio_bus.o \
9 sdio_cis.o sdio_io.o sdio_irq.o \ 9 sdio_cis.o sdio_io.o sdio_irq.o \
10 quirks.o slot-gpio.o 10 quirks.o slot-gpio.o
11mmc_core-$(CONFIG_OF) += pwrseq.o pwrseq_simple.o 11mmc_core-$(CONFIG_OF) += pwrseq.o pwrseq_simple.o pwrseq_emmc.o
12mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o 12mmc_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[] = {
26 { 26 {
27 .compatible = "mmc-pwrseq-simple", 27 .compatible = "mmc-pwrseq-simple",
28 .alloc = mmc_pwrseq_simple_alloc, 28 .alloc = mmc_pwrseq_simple_alloc,
29 }, {
30 .compatible = "mmc-pwrseq-emmc",
31 .alloc = mmc_pwrseq_emmc_alloc,
29 }, 32 },
30}; 33};
31 34
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);
28void mmc_pwrseq_free(struct mmc_host *host); 28void mmc_pwrseq_free(struct mmc_host *host);
29 29
30int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev); 30int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev);
31int mmc_pwrseq_emmc_alloc(struct mmc_host *host, struct device *dev);
31 32
32#else 33#else
33 34
diff --git a/drivers/mmc/core/pwrseq_emmc.c b/drivers/mmc/core/pwrseq_emmc.c
new file mode 100644
index 000000000000..a2d545904fbf
--- /dev/null
+++ b/drivers/mmc/core/pwrseq_emmc.c
@@ -0,0 +1,101 @@
1/*
2 * Copyright (C) 2015, Samsung Electronics Co., Ltd.
3 *
4 * Author: Marek Szyprowski <m.szyprowski@samsung.com>
5 *
6 * License terms: GNU General Public License (GPL) version 2
7 *
8 * Simple eMMC hardware reset provider
9 */
10#include <linux/delay.h>
11#include <linux/kernel.h>
12#include <linux/slab.h>
13#include <linux/device.h>
14#include <linux/err.h>
15#include <linux/gpio/consumer.h>
16#include <linux/reboot.h>
17
18#include <linux/mmc/host.h>
19
20#include "pwrseq.h"
21
22struct mmc_pwrseq_emmc {
23 struct mmc_pwrseq pwrseq;
24 struct notifier_block reset_nb;
25 struct gpio_desc *reset_gpio;
26};
27
28static void __mmc_pwrseq_emmc_reset(struct mmc_pwrseq_emmc *pwrseq)
29{
30 gpiod_set_value(pwrseq->reset_gpio, 1);
31 udelay(1);
32 gpiod_set_value(pwrseq->reset_gpio, 0);
33 udelay(200);
34}
35
36static void mmc_pwrseq_emmc_reset(struct mmc_host *host)
37{
38 struct mmc_pwrseq_emmc *pwrseq = container_of(host->pwrseq,
39 struct mmc_pwrseq_emmc, pwrseq);
40
41 __mmc_pwrseq_emmc_reset(pwrseq);
42}
43
44static void mmc_pwrseq_emmc_free(struct mmc_host *host)
45{
46 struct mmc_pwrseq_emmc *pwrseq = container_of(host->pwrseq,
47 struct mmc_pwrseq_emmc, pwrseq);
48
49 unregister_restart_handler(&pwrseq->reset_nb);
50 gpiod_put(pwrseq->reset_gpio);
51 kfree(pwrseq);
52 host->pwrseq = NULL;
53}
54
55static struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = {
56 .post_power_on = mmc_pwrseq_emmc_reset,
57 .free = mmc_pwrseq_emmc_free,
58};
59
60static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this,
61 unsigned long mode, void *cmd)
62{
63 struct mmc_pwrseq_emmc *pwrseq = container_of(this,
64 struct mmc_pwrseq_emmc, reset_nb);
65
66 __mmc_pwrseq_emmc_reset(pwrseq);
67 return NOTIFY_DONE;
68}
69
70int mmc_pwrseq_emmc_alloc(struct mmc_host *host, struct device *dev)
71{
72 struct mmc_pwrseq_emmc *pwrseq;
73 int ret = 0;
74
75 pwrseq = kzalloc(sizeof(struct mmc_pwrseq_emmc), GFP_KERNEL);
76 if (!pwrseq)
77 return -ENOMEM;
78
79 pwrseq->reset_gpio = gpiod_get_index(dev, "reset", 0, GPIOD_OUT_LOW);
80 if (IS_ERR(pwrseq->reset_gpio)) {
81 ret = PTR_ERR(pwrseq->reset_gpio);
82 goto free;
83 }
84
85 /*
86 * register reset handler to ensure emmc reset also from
87 * emergency_reboot(), priority 129 schedules it just before
88 * system reboot
89 */
90 pwrseq->reset_nb.notifier_call = mmc_pwrseq_emmc_reset_nb;
91 pwrseq->reset_nb.priority = 129;
92 register_restart_handler(&pwrseq->reset_nb);
93
94 pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops;
95 host->pwrseq = &pwrseq->pwrseq;
96
97 return 0;
98free:
99 kfree(pwrseq);
100 return ret;
101}