mirror of
https://github.com/Fishwaldo/build.git
synced 2025-04-01 03:31:36 +00:00
-- Thanks again to @ntemis for bringing these together and letting me know about it - Tinker reboot now seemingly solved by a proper method, should no longer require OF workarounds - various fixes from various sources, see patches for complete information. - boots, still needs debugging of BT/touchscreen/sound - some patch warnings exist, again WIP update for those willing to take a look. (@chwe17 perhaps?)
949 lines
30 KiB
Diff
949 lines
30 KiB
Diff
From 75bb99dc815464846a4add357494acf04212271d Mon Sep 17 00:00:00 2001
|
|
From: Julia Lawall <Julia.Lawall@lip6.fr>
|
|
Date: Sat, 14 Nov 2015 18:05:20 +0100
|
|
Subject: [PATCH] UPSTREAM: mmc: pwrseq: constify mmc_pwrseq_ops structures
|
|
|
|
The mmc_pwrseq_ops structures are never modified, so declare them as const.
|
|
|
|
Done with the help of Coccinelle.
|
|
|
|
Signed-off-by: Julia Lawall <Julia.Lawall@lip6.fr>
|
|
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
|
|
(cherry picked from commit ffedbd2210f2f4cba490a9205adc11fd1b89a852)
|
|
---
|
|
drivers/mmc/core/pwrseq.h | 2 +-
|
|
drivers/mmc/core/pwrseq_emmc.c | 2 +-
|
|
drivers/mmc/core/pwrseq_simple.c | 2 +-
|
|
3 files changed, 3 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/drivers/mmc/core/pwrseq.h b/drivers/mmc/core/pwrseq.h
|
|
index 096da48c6a7e..133de0426687 100644
|
|
--- a/drivers/mmc/core/pwrseq.h
|
|
+++ b/drivers/mmc/core/pwrseq.h
|
|
@@ -16,7 +16,7 @@ struct mmc_pwrseq_ops {
|
|
};
|
|
|
|
struct mmc_pwrseq {
|
|
- struct mmc_pwrseq_ops *ops;
|
|
+ const struct mmc_pwrseq_ops *ops;
|
|
};
|
|
|
|
#ifdef CONFIG_OF
|
|
diff --git a/drivers/mmc/core/pwrseq_emmc.c b/drivers/mmc/core/pwrseq_emmc.c
|
|
index ad4f94ec7e8d..4a82bc77fe49 100644
|
|
--- a/drivers/mmc/core/pwrseq_emmc.c
|
|
+++ b/drivers/mmc/core/pwrseq_emmc.c
|
|
@@ -51,7 +51,7 @@ static void mmc_pwrseq_emmc_free(struct mmc_host *host)
|
|
kfree(pwrseq);
|
|
}
|
|
|
|
-static struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = {
|
|
+static const struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = {
|
|
.post_power_on = mmc_pwrseq_emmc_reset,
|
|
.free = mmc_pwrseq_emmc_free,
|
|
};
|
|
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
|
|
index d10538bb5e07..2b16263458af 100644
|
|
--- a/drivers/mmc/core/pwrseq_simple.c
|
|
+++ b/drivers/mmc/core/pwrseq_simple.c
|
|
@@ -87,7 +87,7 @@ static void mmc_pwrseq_simple_free(struct mmc_host *host)
|
|
kfree(pwrseq);
|
|
}
|
|
|
|
-static struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = {
|
|
+static const struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = {
|
|
.pre_power_on = mmc_pwrseq_simple_pre_power_on,
|
|
.post_power_on = mmc_pwrseq_simple_post_power_on,
|
|
.power_off = mmc_pwrseq_simple_power_off,
|
|
|
|
From 1977551c6ef29f55b398a02112e3075c9a38649d Mon Sep 17 00:00:00 2001
|
|
From: Martin Fuzzey <mfuzzey@parkeon.com>
|
|
Date: Wed, 20 Jan 2016 16:08:03 +0100
|
|
Subject: [PATCH] UPSTREAM: mmc: pwrseq_simple: Make reset-gpios optional to
|
|
match doc
|
|
|
|
The DT binding doc says reset-gpios is an optional property but the code
|
|
currently bails out if it is omitted.
|
|
|
|
This is a regression since it breaks previously working device trees.
|
|
Fix it by restoring the original documented behaviour.
|
|
|
|
Fixes: ce037275861e ("mmc: pwrseq_simple: use GPIO descriptors array API")
|
|
Tested-by: Tony Lindgren <tony@atomide.com>
|
|
Signed-off-by: Martin Fuzzey <mfuzzey@parkeon.com>
|
|
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
|
|
(cherry picked from commit 64a67d4762ce3ce4c9466eadd152d825fbf84967)
|
|
---
|
|
drivers/mmc/core/pwrseq_simple.c | 22 ++++++++++++++--------
|
|
1 file changed, 14 insertions(+), 8 deletions(-)
|
|
|
|
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
|
|
index 2b16263458af..aba786daebca 100644
|
|
--- a/drivers/mmc/core/pwrseq_simple.c
|
|
+++ b/drivers/mmc/core/pwrseq_simple.c
|
|
@@ -29,15 +29,18 @@ struct mmc_pwrseq_simple {
|
|
static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
|
|
int value)
|
|
{
|
|
- int i;
|
|
struct gpio_descs *reset_gpios = pwrseq->reset_gpios;
|
|
- int values[reset_gpios->ndescs];
|
|
|
|
- for (i = 0; i < reset_gpios->ndescs; i++)
|
|
- values[i] = value;
|
|
+ if (!IS_ERR(reset_gpios)) {
|
|
+ int i;
|
|
+ int values[reset_gpios->ndescs];
|
|
|
|
- gpiod_set_array_value_cansleep(reset_gpios->ndescs, reset_gpios->desc,
|
|
- values);
|
|
+ for (i = 0; i < reset_gpios->ndescs; i++)
|
|
+ values[i] = value;
|
|
+
|
|
+ gpiod_set_array_value_cansleep(
|
|
+ reset_gpios->ndescs, reset_gpios->desc, values);
|
|
+ }
|
|
}
|
|
|
|
static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host)
|
|
@@ -79,7 +82,8 @@ static void mmc_pwrseq_simple_free(struct mmc_host *host)
|
|
struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq,
|
|
struct mmc_pwrseq_simple, pwrseq);
|
|
|
|
- gpiod_put_array(pwrseq->reset_gpios);
|
|
+ if (!IS_ERR(pwrseq->reset_gpios))
|
|
+ gpiod_put_array(pwrseq->reset_gpios);
|
|
|
|
if (!IS_ERR(pwrseq->ext_clk))
|
|
clk_put(pwrseq->ext_clk);
|
|
@@ -112,7 +116,9 @@ struct mmc_pwrseq *mmc_pwrseq_simple_alloc(struct mmc_host *host,
|
|
}
|
|
|
|
pwrseq->reset_gpios = gpiod_get_array(dev, "reset", GPIOD_OUT_HIGH);
|
|
- if (IS_ERR(pwrseq->reset_gpios)) {
|
|
+ if (IS_ERR(pwrseq->reset_gpios) &&
|
|
+ PTR_ERR(pwrseq->reset_gpios) != -ENOENT &&
|
|
+ PTR_ERR(pwrseq->reset_gpios) != -ENOSYS) {
|
|
ret = PTR_ERR(pwrseq->reset_gpios);
|
|
goto clk_put;
|
|
}
|
|
|
|
From e79ed0004dc68dc2f2189256bf00a1f579c78f1a Mon Sep 17 00:00:00 2001
|
|
From: Peter Chen <peter.chen@freescale.com>
|
|
Date: Wed, 6 Jan 2016 11:34:10 +0800
|
|
Subject: [PATCH] UPSTREAM: mmc: core: pwrseq_simple: remove unused header file
|
|
|
|
Signed-off-by: Peter Chen <peter.chen@freescale.com>
|
|
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
|
|
(cherry picked from commit 62c03ca3ffa1ddf55a66411be02f7e4678771fce)
|
|
---
|
|
drivers/mmc/core/pwrseq_simple.c | 1 -
|
|
1 file changed, 1 deletion(-)
|
|
|
|
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
|
|
index aba786daebca..bc173e18b71c 100644
|
|
--- a/drivers/mmc/core/pwrseq_simple.c
|
|
+++ b/drivers/mmc/core/pwrseq_simple.c
|
|
@@ -12,7 +12,6 @@
|
|
#include <linux/slab.h>
|
|
#include <linux/device.h>
|
|
#include <linux/err.h>
|
|
-#include <linux/of_gpio.h>
|
|
#include <linux/gpio/consumer.h>
|
|
|
|
#include <linux/mmc/host.h>
|
|
|
|
From 545d059f7a0a4c470acfdb0fff30397899597f09 Mon Sep 17 00:00:00 2001
|
|
From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
|
|
Date: Thu, 14 Apr 2016 14:02:14 +0100
|
|
Subject: [PATCH] UPSTREAM: mmc: pwrseq_simple: add to_pwrseq_simple() macro
|
|
|
|
This patch adds to_pwrseq_simple() macro to make the code more readable.
|
|
|
|
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
|
|
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
|
|
(cherry picked from commit 5b96fea730ab79bdf6f8071cadf8208296bf5e8d)
|
|
---
|
|
drivers/mmc/core/pwrseq_simple.c | 14 ++++++--------
|
|
1 file changed, 6 insertions(+), 8 deletions(-)
|
|
|
|
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
|
|
index bc173e18b71c..f94271bb1f6b 100644
|
|
--- a/drivers/mmc/core/pwrseq_simple.c
|
|
+++ b/drivers/mmc/core/pwrseq_simple.c
|
|
@@ -25,6 +25,8 @@ struct mmc_pwrseq_simple {
|
|
struct gpio_descs *reset_gpios;
|
|
};
|
|
|
|
+#define to_pwrseq_simple(p) container_of(p, struct mmc_pwrseq_simple, pwrseq)
|
|
+
|
|
static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
|
|
int value)
|
|
{
|
|
@@ -44,8 +46,7 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
|
|
|
|
static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host)
|
|
{
|
|
- struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq,
|
|
- struct mmc_pwrseq_simple, pwrseq);
|
|
+ struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq);
|
|
|
|
if (!IS_ERR(pwrseq->ext_clk) && !pwrseq->clk_enabled) {
|
|
clk_prepare_enable(pwrseq->ext_clk);
|
|
@@ -57,16 +58,14 @@ static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host)
|
|
|
|
static void mmc_pwrseq_simple_post_power_on(struct mmc_host *host)
|
|
{
|
|
- struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq,
|
|
- struct mmc_pwrseq_simple, pwrseq);
|
|
+ struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq);
|
|
|
|
mmc_pwrseq_simple_set_gpios_value(pwrseq, 0);
|
|
}
|
|
|
|
static void mmc_pwrseq_simple_power_off(struct mmc_host *host)
|
|
{
|
|
- struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq,
|
|
- struct mmc_pwrseq_simple, pwrseq);
|
|
+ struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq);
|
|
|
|
mmc_pwrseq_simple_set_gpios_value(pwrseq, 1);
|
|
|
|
@@ -78,8 +77,7 @@ static void mmc_pwrseq_simple_power_off(struct mmc_host *host)
|
|
|
|
static void mmc_pwrseq_simple_free(struct mmc_host *host)
|
|
{
|
|
- struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq,
|
|
- struct mmc_pwrseq_simple, pwrseq);
|
|
+ struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq);
|
|
|
|
if (!IS_ERR(pwrseq->reset_gpios))
|
|
gpiod_put_array(pwrseq->reset_gpios);
|
|
|
|
From e8c5f0b9383e6a528c8fc00d61755f8187e4c0b8 Mon Sep 17 00:00:00 2001
|
|
From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
|
|
Date: Thu, 14 Apr 2016 14:02:15 +0100
|
|
Subject: [PATCH] UPSTREAM: mmc: pwrseq_emmc: add to_pwrseq_emmc() macro
|
|
|
|
This patch adds to_pwrseq_emmc() macro to make the code more readable.
|
|
|
|
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
|
|
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
|
|
(cherry picked from commit f01b72d0fd53b61cafd25b16d15e18b1ef8ae065)
|
|
---
|
|
drivers/mmc/core/pwrseq_emmc.c | 8 ++++----
|
|
1 file changed, 4 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/drivers/mmc/core/pwrseq_emmc.c b/drivers/mmc/core/pwrseq_emmc.c
|
|
index 4a82bc77fe49..c2d732aa464c 100644
|
|
--- a/drivers/mmc/core/pwrseq_emmc.c
|
|
+++ b/drivers/mmc/core/pwrseq_emmc.c
|
|
@@ -25,6 +25,8 @@ struct mmc_pwrseq_emmc {
|
|
struct gpio_desc *reset_gpio;
|
|
};
|
|
|
|
+#define to_pwrseq_emmc(p) container_of(p, struct mmc_pwrseq_emmc, pwrseq)
|
|
+
|
|
static void __mmc_pwrseq_emmc_reset(struct mmc_pwrseq_emmc *pwrseq)
|
|
{
|
|
gpiod_set_value(pwrseq->reset_gpio, 1);
|
|
@@ -35,16 +37,14 @@ static void __mmc_pwrseq_emmc_reset(struct mmc_pwrseq_emmc *pwrseq)
|
|
|
|
static void mmc_pwrseq_emmc_reset(struct mmc_host *host)
|
|
{
|
|
- struct mmc_pwrseq_emmc *pwrseq = container_of(host->pwrseq,
|
|
- struct mmc_pwrseq_emmc, pwrseq);
|
|
+ struct mmc_pwrseq_emmc *pwrseq = to_pwrseq_emmc(host->pwrseq);
|
|
|
|
__mmc_pwrseq_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);
|
|
+ struct mmc_pwrseq_emmc *pwrseq = to_pwrseq_emmc(host->pwrseq);
|
|
|
|
unregister_restart_handler(&pwrseq->reset_nb);
|
|
gpiod_put(pwrseq->reset_gpio);
|
|
|
|
From ef2f3c5b7375b930697a64c85f30f9109e631cb0 Mon Sep 17 00:00:00 2001
|
|
From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
|
|
Date: Thu, 14 Apr 2016 14:02:16 +0100
|
|
Subject: [PATCH] UPSTREAM: mmc: pwrseq: convert to proper platform device
|
|
|
|
simple-pwrseq and emmc-pwrseq drivers rely on platform_device
|
|
structure from of_find_device_by_node(), this works mostly. But, as there
|
|
is no driver associated with this devices, cases like default/init pinctrl
|
|
setup would never be performed by pwrseq. This becomes problem when the
|
|
gpios used in pwrseq require pinctrl setup.
|
|
|
|
Currently most of the common pinctrl setup is done in
|
|
drivers/base/pinctrl.c by pinctrl_bind_pins().
|
|
|
|
There are two ways to solve this issue on either convert pwrseq drivers
|
|
to a proper platform drivers or copy the exact code from
|
|
pcintrl_bind_pins(). I prefer converting pwrseq to proper drivers so that
|
|
other cases like setting up clks/parents from dt would also be possible.
|
|
|
|
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
|
|
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
|
|
(cherry picked from commit d97a1e5d7cd2b5b0edc02a40fe6897b710c9e10f)
|
|
---
|
|
drivers/mmc/core/Kconfig | 22 ++++++++
|
|
drivers/mmc/core/Makefile | 4 +-
|
|
drivers/mmc/core/pwrseq.c | 108 ++++++++++++++++++---------------------
|
|
drivers/mmc/core/pwrseq.h | 19 ++++---
|
|
drivers/mmc/core/pwrseq_emmc.c | 75 +++++++++++++++++----------
|
|
drivers/mmc/core/pwrseq_simple.c | 79 +++++++++++++++-------------
|
|
6 files changed, 178 insertions(+), 129 deletions(-)
|
|
|
|
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig
|
|
index 87cc07dedd9f..00dfaea06003 100644
|
|
--- a/drivers/mmc/core/Kconfig
|
|
+++ b/drivers/mmc/core/Kconfig
|
|
@@ -16,3 +16,25 @@ config MMC_PARANOID_SD_INIT
|
|
about re-trying SD init requests. This can be a useful
|
|
work-around for buggy controllers and hardware. Enable
|
|
if you are experiencing issues with SD detection.
|
|
+
|
|
+config PWRSEQ_EMMC
|
|
+ tristate "HW reset support for eMMC"
|
|
+ default y
|
|
+ depends on OF
|
|
+ help
|
|
+ This selects Hardware reset support aka pwrseq-emmc for eMMC
|
|
+ devices. By default this option is set to y.
|
|
+
|
|
+ This driver can also be built as a module. If so, the module
|
|
+ will be called pwrseq_emmc.
|
|
+
|
|
+config PWRSEQ_SIMPLE
|
|
+ tristate "Simple HW reset support for MMC"
|
|
+ default y
|
|
+ depends on OF
|
|
+ help
|
|
+ This selects simple hardware reset support aka pwrseq-simple for MMC
|
|
+ devices. By default this option is set to y.
|
|
+
|
|
+ This driver can also be built as a module. If so, the module
|
|
+ will be called pwrseq_simple.
|
|
diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile
|
|
index 2c25138f28b7..f007151dfdc6 100644
|
|
--- a/drivers/mmc/core/Makefile
|
|
+++ b/drivers/mmc/core/Makefile
|
|
@@ -8,5 +8,7 @@ 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 pwrseq_emmc.o
|
|
+mmc_core-$(CONFIG_OF) += pwrseq.o
|
|
+obj-$(CONFIG_PWRSEQ_SIMPLE) += pwrseq_simple.o
|
|
+obj-$(CONFIG_PWRSEQ_EMMC) += 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 4c1d1757dbf9..9386c4771814 100644
|
|
--- a/drivers/mmc/core/pwrseq.c
|
|
+++ b/drivers/mmc/core/pwrseq.c
|
|
@@ -8,88 +8,55 @@
|
|
* MMC power sequence management
|
|
*/
|
|
#include <linux/kernel.h>
|
|
-#include <linux/platform_device.h>
|
|
#include <linux/err.h>
|
|
+#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
-#include <linux/of_platform.h>
|
|
|
|
#include <linux/mmc/host.h>
|
|
|
|
#include "pwrseq.h"
|
|
|
|
-struct mmc_pwrseq_match {
|
|
- const char *compatible;
|
|
- struct mmc_pwrseq *(*alloc)(struct mmc_host *host, struct device *dev);
|
|
-};
|
|
-
|
|
-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,
|
|
- },
|
|
-};
|
|
-
|
|
-static struct mmc_pwrseq_match *mmc_pwrseq_find(struct device_node *np)
|
|
-{
|
|
- struct mmc_pwrseq_match *match = ERR_PTR(-ENODEV);
|
|
- int i;
|
|
-
|
|
- for (i = 0; i < ARRAY_SIZE(pwrseq_match); i++) {
|
|
- if (of_device_is_compatible(np, pwrseq_match[i].compatible)) {
|
|
- match = &pwrseq_match[i];
|
|
- break;
|
|
- }
|
|
- }
|
|
-
|
|
- return match;
|
|
-}
|
|
+static DEFINE_MUTEX(pwrseq_list_mutex);
|
|
+static LIST_HEAD(pwrseq_list);
|
|
|
|
int mmc_pwrseq_alloc(struct mmc_host *host)
|
|
{
|
|
- struct platform_device *pdev;
|
|
struct device_node *np;
|
|
- struct mmc_pwrseq_match *match;
|
|
- struct mmc_pwrseq *pwrseq;
|
|
- int ret = 0;
|
|
+ struct mmc_pwrseq *p;
|
|
|
|
np = of_parse_phandle(host->parent->of_node, "mmc-pwrseq", 0);
|
|
if (!np)
|
|
return 0;
|
|
|
|
- pdev = of_find_device_by_node(np);
|
|
- if (!pdev) {
|
|
- ret = -ENODEV;
|
|
- goto err;
|
|
- }
|
|
+ mutex_lock(&pwrseq_list_mutex);
|
|
+ list_for_each_entry(p, &pwrseq_list, pwrseq_node) {
|
|
+ if (p->dev->of_node == np) {
|
|
+ if (!try_module_get(p->owner))
|
|
+ dev_err(host->parent,
|
|
+ "increasing module refcount failed\n");
|
|
+ else
|
|
+ host->pwrseq = p;
|
|
|
|
- match = mmc_pwrseq_find(np);
|
|
- if (IS_ERR(match)) {
|
|
- ret = PTR_ERR(match);
|
|
- goto err;
|
|
+ break;
|
|
+ }
|
|
}
|
|
|
|
- pwrseq = match->alloc(host, &pdev->dev);
|
|
- if (IS_ERR(pwrseq)) {
|
|
- ret = PTR_ERR(pwrseq);
|
|
- goto err;
|
|
- }
|
|
+ of_node_put(np);
|
|
+ mutex_unlock(&pwrseq_list_mutex);
|
|
+
|
|
+ if (!host->pwrseq)
|
|
+ return -EPROBE_DEFER;
|
|
|
|
- host->pwrseq = pwrseq;
|
|
dev_info(host->parent, "allocated mmc-pwrseq\n");
|
|
|
|
-err:
|
|
- of_node_put(np);
|
|
- return ret;
|
|
+ return 0;
|
|
}
|
|
|
|
void mmc_pwrseq_pre_power_on(struct mmc_host *host)
|
|
{
|
|
struct mmc_pwrseq *pwrseq = host->pwrseq;
|
|
|
|
- if (pwrseq && pwrseq->ops && pwrseq->ops->pre_power_on)
|
|
+ if (pwrseq && pwrseq->ops->pre_power_on)
|
|
pwrseq->ops->pre_power_on(host);
|
|
}
|
|
|
|
@@ -97,7 +64,7 @@ void mmc_pwrseq_post_power_on(struct mmc_host *host)
|
|
{
|
|
struct mmc_pwrseq *pwrseq = host->pwrseq;
|
|
|
|
- if (pwrseq && pwrseq->ops && pwrseq->ops->post_power_on)
|
|
+ if (pwrseq && pwrseq->ops->post_power_on)
|
|
pwrseq->ops->post_power_on(host);
|
|
}
|
|
|
|
@@ -105,7 +72,7 @@ void mmc_pwrseq_power_off(struct mmc_host *host)
|
|
{
|
|
struct mmc_pwrseq *pwrseq = host->pwrseq;
|
|
|
|
- if (pwrseq && pwrseq->ops && pwrseq->ops->power_off)
|
|
+ if (pwrseq && pwrseq->ops->power_off)
|
|
pwrseq->ops->power_off(host);
|
|
}
|
|
|
|
@@ -113,8 +80,31 @@ void mmc_pwrseq_free(struct mmc_host *host)
|
|
{
|
|
struct mmc_pwrseq *pwrseq = host->pwrseq;
|
|
|
|
- if (pwrseq && pwrseq->ops && pwrseq->ops->free)
|
|
- pwrseq->ops->free(host);
|
|
+ if (pwrseq) {
|
|
+ module_put(pwrseq->owner);
|
|
+ host->pwrseq = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+int mmc_pwrseq_register(struct mmc_pwrseq *pwrseq)
|
|
+{
|
|
+ if (!pwrseq || !pwrseq->ops || !pwrseq->dev)
|
|
+ return -EINVAL;
|
|
|
|
- host->pwrseq = NULL;
|
|
+ mutex_lock(&pwrseq_list_mutex);
|
|
+ list_add(&pwrseq->pwrseq_node, &pwrseq_list);
|
|
+ mutex_unlock(&pwrseq_list_mutex);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(mmc_pwrseq_register);
|
|
+
|
|
+void mmc_pwrseq_unregister(struct mmc_pwrseq *pwrseq)
|
|
+{
|
|
+ if (pwrseq) {
|
|
+ mutex_lock(&pwrseq_list_mutex);
|
|
+ list_del(&pwrseq->pwrseq_node);
|
|
+ mutex_unlock(&pwrseq_list_mutex);
|
|
+ }
|
|
}
|
|
+EXPORT_SYMBOL_GPL(mmc_pwrseq_unregister);
|
|
diff --git a/drivers/mmc/core/pwrseq.h b/drivers/mmc/core/pwrseq.h
|
|
index 133de0426687..d69e751f148b 100644
|
|
--- a/drivers/mmc/core/pwrseq.h
|
|
+++ b/drivers/mmc/core/pwrseq.h
|
|
@@ -8,32 +8,39 @@
|
|
#ifndef _MMC_CORE_PWRSEQ_H
|
|
#define _MMC_CORE_PWRSEQ_H
|
|
|
|
+#include <linux/mmc/host.h>
|
|
+
|
|
struct mmc_pwrseq_ops {
|
|
void (*pre_power_on)(struct mmc_host *host);
|
|
void (*post_power_on)(struct mmc_host *host);
|
|
void (*power_off)(struct mmc_host *host);
|
|
- void (*free)(struct mmc_host *host);
|
|
};
|
|
|
|
struct mmc_pwrseq {
|
|
const struct mmc_pwrseq_ops *ops;
|
|
+ struct device *dev;
|
|
+ struct list_head pwrseq_node;
|
|
+ struct module *owner;
|
|
};
|
|
|
|
#ifdef CONFIG_OF
|
|
|
|
+int mmc_pwrseq_register(struct mmc_pwrseq *pwrseq);
|
|
+void mmc_pwrseq_unregister(struct mmc_pwrseq *pwrseq);
|
|
+
|
|
int mmc_pwrseq_alloc(struct mmc_host *host);
|
|
void mmc_pwrseq_pre_power_on(struct mmc_host *host);
|
|
void mmc_pwrseq_post_power_on(struct mmc_host *host);
|
|
void mmc_pwrseq_power_off(struct mmc_host *host);
|
|
void mmc_pwrseq_free(struct mmc_host *host);
|
|
|
|
-struct mmc_pwrseq *mmc_pwrseq_simple_alloc(struct mmc_host *host,
|
|
- struct device *dev);
|
|
-struct mmc_pwrseq *mmc_pwrseq_emmc_alloc(struct mmc_host *host,
|
|
- struct device *dev);
|
|
-
|
|
#else
|
|
|
|
+static inline int mmc_pwrseq_register(struct mmc_pwrseq *pwrseq)
|
|
+{
|
|
+ return -ENOSYS;
|
|
+}
|
|
+static inline void mmc_pwrseq_unregister(struct mmc_pwrseq *pwrseq) {}
|
|
static inline int mmc_pwrseq_alloc(struct mmc_host *host) { return 0; }
|
|
static inline void mmc_pwrseq_pre_power_on(struct mmc_host *host) {}
|
|
static inline void mmc_pwrseq_post_power_on(struct mmc_host *host) {}
|
|
diff --git a/drivers/mmc/core/pwrseq_emmc.c b/drivers/mmc/core/pwrseq_emmc.c
|
|
index c2d732aa464c..adc9c0c614fb 100644
|
|
--- a/drivers/mmc/core/pwrseq_emmc.c
|
|
+++ b/drivers/mmc/core/pwrseq_emmc.c
|
|
@@ -9,6 +9,9 @@
|
|
*/
|
|
#include <linux/delay.h>
|
|
#include <linux/kernel.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/device.h>
|
|
#include <linux/err.h>
|
|
@@ -42,20 +45,6 @@ static void mmc_pwrseq_emmc_reset(struct mmc_host *host)
|
|
__mmc_pwrseq_emmc_reset(pwrseq);
|
|
}
|
|
|
|
-static void mmc_pwrseq_emmc_free(struct mmc_host *host)
|
|
-{
|
|
- struct mmc_pwrseq_emmc *pwrseq = to_pwrseq_emmc(host->pwrseq);
|
|
-
|
|
- unregister_restart_handler(&pwrseq->reset_nb);
|
|
- gpiod_put(pwrseq->reset_gpio);
|
|
- kfree(pwrseq);
|
|
-}
|
|
-
|
|
-static const struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = {
|
|
- .post_power_on = 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)
|
|
{
|
|
@@ -66,21 +55,22 @@ static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this,
|
|
return NOTIFY_DONE;
|
|
}
|
|
|
|
-struct mmc_pwrseq *mmc_pwrseq_emmc_alloc(struct mmc_host *host,
|
|
- struct device *dev)
|
|
+static const struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = {
|
|
+ .post_power_on = mmc_pwrseq_emmc_reset,
|
|
+};
|
|
+
|
|
+static int mmc_pwrseq_emmc_probe(struct platform_device *pdev)
|
|
{
|
|
struct mmc_pwrseq_emmc *pwrseq;
|
|
- int ret = 0;
|
|
+ struct device *dev = &pdev->dev;
|
|
|
|
- pwrseq = kzalloc(sizeof(struct mmc_pwrseq_emmc), GFP_KERNEL);
|
|
+ pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL);
|
|
if (!pwrseq)
|
|
- return ERR_PTR(-ENOMEM);
|
|
+ return -ENOMEM;
|
|
|
|
- pwrseq->reset_gpio = gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
|
- if (IS_ERR(pwrseq->reset_gpio)) {
|
|
- ret = PTR_ERR(pwrseq->reset_gpio);
|
|
- goto free;
|
|
- }
|
|
+ pwrseq->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
|
+ if (IS_ERR(pwrseq->reset_gpio))
|
|
+ return PTR_ERR(pwrseq->reset_gpio);
|
|
|
|
/*
|
|
* register reset handler to ensure emmc reset also from
|
|
@@ -92,9 +82,38 @@ struct mmc_pwrseq *mmc_pwrseq_emmc_alloc(struct mmc_host *host,
|
|
register_restart_handler(&pwrseq->reset_nb);
|
|
|
|
pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops;
|
|
+ pwrseq->pwrseq.dev = dev;
|
|
+ pwrseq->pwrseq.owner = THIS_MODULE;
|
|
+ platform_set_drvdata(pdev, pwrseq);
|
|
+
|
|
+ return mmc_pwrseq_register(&pwrseq->pwrseq);
|
|
+}
|
|
+
|
|
+static int mmc_pwrseq_emmc_remove(struct platform_device *pdev)
|
|
+{
|
|
+ struct mmc_pwrseq_emmc *pwrseq = platform_get_drvdata(pdev);
|
|
+
|
|
+ unregister_restart_handler(&pwrseq->reset_nb);
|
|
+ mmc_pwrseq_unregister(&pwrseq->pwrseq);
|
|
|
|
- return &pwrseq->pwrseq;
|
|
-free:
|
|
- kfree(pwrseq);
|
|
- return ERR_PTR(ret);
|
|
+ return 0;
|
|
}
|
|
+
|
|
+static const struct of_device_id mmc_pwrseq_emmc_of_match[] = {
|
|
+ { .compatible = "mmc-pwrseq-emmc",},
|
|
+ {/* sentinel */},
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(of, mmc_pwrseq_emmc_of_match);
|
|
+
|
|
+static struct platform_driver mmc_pwrseq_emmc_driver = {
|
|
+ .probe = mmc_pwrseq_emmc_probe,
|
|
+ .remove = mmc_pwrseq_emmc_remove,
|
|
+ .driver = {
|
|
+ .name = "pwrseq_emmc",
|
|
+ .of_match_table = mmc_pwrseq_emmc_of_match,
|
|
+ },
|
|
+};
|
|
+
|
|
+module_platform_driver(mmc_pwrseq_emmc_driver);
|
|
+MODULE_LICENSE("GPL v2");
|
|
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
|
|
index f94271bb1f6b..450d907c6e6c 100644
|
|
--- a/drivers/mmc/core/pwrseq_simple.c
|
|
+++ b/drivers/mmc/core/pwrseq_simple.c
|
|
@@ -8,7 +8,10 @@
|
|
* Simple MMC power sequence management
|
|
*/
|
|
#include <linux/clk.h>
|
|
+#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/device.h>
|
|
#include <linux/err.h>
|
|
@@ -75,58 +78,64 @@ static void mmc_pwrseq_simple_power_off(struct mmc_host *host)
|
|
}
|
|
}
|
|
|
|
-static void mmc_pwrseq_simple_free(struct mmc_host *host)
|
|
-{
|
|
- struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq);
|
|
-
|
|
- if (!IS_ERR(pwrseq->reset_gpios))
|
|
- gpiod_put_array(pwrseq->reset_gpios);
|
|
-
|
|
- if (!IS_ERR(pwrseq->ext_clk))
|
|
- clk_put(pwrseq->ext_clk);
|
|
-
|
|
- kfree(pwrseq);
|
|
-}
|
|
-
|
|
static const struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = {
|
|
.pre_power_on = mmc_pwrseq_simple_pre_power_on,
|
|
.post_power_on = mmc_pwrseq_simple_post_power_on,
|
|
.power_off = mmc_pwrseq_simple_power_off,
|
|
- .free = mmc_pwrseq_simple_free,
|
|
};
|
|
|
|
-struct mmc_pwrseq *mmc_pwrseq_simple_alloc(struct mmc_host *host,
|
|
- struct device *dev)
|
|
+static const struct of_device_id mmc_pwrseq_simple_of_match[] = {
|
|
+ { .compatible = "mmc-pwrseq-simple",},
|
|
+ {/* sentinel */},
|
|
+};
|
|
+MODULE_DEVICE_TABLE(of, mmc_pwrseq_simple_of_match);
|
|
+
|
|
+static int mmc_pwrseq_simple_probe(struct platform_device *pdev)
|
|
{
|
|
struct mmc_pwrseq_simple *pwrseq;
|
|
- int ret = 0;
|
|
+ struct device *dev = &pdev->dev;
|
|
|
|
- pwrseq = kzalloc(sizeof(*pwrseq), GFP_KERNEL);
|
|
+ pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL);
|
|
if (!pwrseq)
|
|
- return ERR_PTR(-ENOMEM);
|
|
+ return -ENOMEM;
|
|
|
|
- pwrseq->ext_clk = clk_get(dev, "ext_clock");
|
|
- if (IS_ERR(pwrseq->ext_clk) &&
|
|
- PTR_ERR(pwrseq->ext_clk) != -ENOENT) {
|
|
- ret = PTR_ERR(pwrseq->ext_clk);
|
|
- goto free;
|
|
- }
|
|
+ pwrseq->ext_clk = devm_clk_get(dev, "ext_clock");
|
|
+ if (IS_ERR(pwrseq->ext_clk) && PTR_ERR(pwrseq->ext_clk) != -ENOENT)
|
|
+ return PTR_ERR(pwrseq->ext_clk);
|
|
|
|
- pwrseq->reset_gpios = gpiod_get_array(dev, "reset", GPIOD_OUT_HIGH);
|
|
+ pwrseq->reset_gpios = devm_gpiod_get_array(dev, "reset",
|
|
+ GPIOD_OUT_HIGH);
|
|
if (IS_ERR(pwrseq->reset_gpios) &&
|
|
PTR_ERR(pwrseq->reset_gpios) != -ENOENT &&
|
|
PTR_ERR(pwrseq->reset_gpios) != -ENOSYS) {
|
|
- ret = PTR_ERR(pwrseq->reset_gpios);
|
|
- goto clk_put;
|
|
+ return PTR_ERR(pwrseq->reset_gpios);
|
|
}
|
|
|
|
+ pwrseq->pwrseq.dev = dev;
|
|
pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops;
|
|
+ pwrseq->pwrseq.owner = THIS_MODULE;
|
|
+ platform_set_drvdata(pdev, pwrseq);
|
|
|
|
- return &pwrseq->pwrseq;
|
|
-clk_put:
|
|
- if (!IS_ERR(pwrseq->ext_clk))
|
|
- clk_put(pwrseq->ext_clk);
|
|
-free:
|
|
- kfree(pwrseq);
|
|
- return ERR_PTR(ret);
|
|
+ return mmc_pwrseq_register(&pwrseq->pwrseq);
|
|
}
|
|
+
|
|
+static int mmc_pwrseq_simple_remove(struct platform_device *pdev)
|
|
+{
|
|
+ struct mmc_pwrseq_simple *pwrseq = platform_get_drvdata(pdev);
|
|
+
|
|
+ mmc_pwrseq_unregister(&pwrseq->pwrseq);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct platform_driver mmc_pwrseq_simple_driver = {
|
|
+ .probe = mmc_pwrseq_simple_probe,
|
|
+ .remove = mmc_pwrseq_simple_remove,
|
|
+ .driver = {
|
|
+ .name = "pwrseq_simple",
|
|
+ .of_match_table = mmc_pwrseq_simple_of_match,
|
|
+ },
|
|
+};
|
|
+
|
|
+module_platform_driver(mmc_pwrseq_simple_driver);
|
|
+MODULE_LICENSE("GPL v2");
|
|
|
|
From 42eb02ddb70002e4f72fa627037b6acbdd4cb7a1 Mon Sep 17 00:00:00 2001
|
|
From: Hans de Goede <hdegoede@redhat.com>
|
|
Date: Sun, 7 Aug 2016 21:02:38 +0200
|
|
Subject: [PATCH] UPSTREAM: mmc: pwrseq-simple: Add an optional
|
|
post-power-on-delay
|
|
|
|
Some devices need a while to boot their firmware after providing clks /
|
|
de-asserting resets before they are ready to receive sdio commands.
|
|
|
|
This commits adds a post-power-on-delay-ms devicetree property to
|
|
mmc-pwrseq-simple for use with such devices.
|
|
|
|
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
|
Acked-by: Rob Herring <robh@kernel.org>
|
|
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
|
|
(cherry picked from commit 721e0497172f0fa661eed2d63367cddf479f35e8)
|
|
---
|
|
Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt | 2 ++
|
|
drivers/mmc/core/pwrseq_simple.c | 9 +++++++++
|
|
2 files changed, 11 insertions(+)
|
|
|
|
diff --git a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt
|
|
index ce0e76749671..e25436861867 100644
|
|
--- a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt
|
|
+++ b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt
|
|
@@ -16,6 +16,8 @@ Optional properties:
|
|
See ../clocks/clock-bindings.txt for details.
|
|
- clock-names : Must include the following entry:
|
|
"ext_clock" (External clock provided to the card).
|
|
+- post-power-on-delay-ms : Delay in ms after powering the card and
|
|
+ de-asserting the reset-gpios (if any)
|
|
|
|
Example:
|
|
|
|
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
|
|
index 450d907c6e6c..1304160de168 100644
|
|
--- a/drivers/mmc/core/pwrseq_simple.c
|
|
+++ b/drivers/mmc/core/pwrseq_simple.c
|
|
@@ -16,6 +16,8 @@
|
|
#include <linux/device.h>
|
|
#include <linux/err.h>
|
|
#include <linux/gpio/consumer.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/property.h>
|
|
|
|
#include <linux/mmc/host.h>
|
|
|
|
@@ -24,6 +26,7 @@
|
|
struct mmc_pwrseq_simple {
|
|
struct mmc_pwrseq pwrseq;
|
|
bool clk_enabled;
|
|
+ u32 post_power_on_delay_ms;
|
|
struct clk *ext_clk;
|
|
struct gpio_descs *reset_gpios;
|
|
};
|
|
@@ -64,6 +67,9 @@ static void mmc_pwrseq_simple_post_power_on(struct mmc_host *host)
|
|
struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq);
|
|
|
|
mmc_pwrseq_simple_set_gpios_value(pwrseq, 0);
|
|
+
|
|
+ if (pwrseq->post_power_on_delay_ms)
|
|
+ msleep(pwrseq->post_power_on_delay_ms);
|
|
}
|
|
|
|
static void mmc_pwrseq_simple_power_off(struct mmc_host *host)
|
|
@@ -111,6 +117,9 @@ static int mmc_pwrseq_simple_probe(struct platform_device *pdev)
|
|
return PTR_ERR(pwrseq->reset_gpios);
|
|
}
|
|
|
|
+ device_property_read_u32(dev, "post-power-on-delay-ms",
|
|
+ &pwrseq->post_power_on_delay_ms);
|
|
+
|
|
pwrseq->pwrseq.dev = dev;
|
|
pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops;
|
|
pwrseq->pwrseq.owner = THIS_MODULE;
|
|
|
|
From bf90ebd56d6f327f77bd7add55b3593679cd5c67 Mon Sep 17 00:00:00 2001
|
|
From: Ulf Hansson <ulf.hansson@linaro.org>
|
|
Date: Sat, 6 May 2017 11:41:30 +0200
|
|
Subject: [PATCH] UPSTREAM: mmc: dt: pwrseq-simple: Invent power-off-delay-us
|
|
|
|
During power off, after the GPIO pin has been asserted, some devices like
|
|
the Wifi chip from TI, Wl18xx, needs a delay before the host continues with
|
|
clock gating and turning off regulators as to follow a graceful shutdown
|
|
sequence.
|
|
|
|
Therefore invent an optional power-off-delay-us DT binding for
|
|
mmc-pwrseq-simple, to allow us to support this constraint.
|
|
|
|
Cc: devicetree@vger.kernel.org
|
|
Cc: Rob Herring <robh+dt@kernel.org>
|
|
Cc: linux-mmc@vger.kernel.org
|
|
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
|
|
Acked-by: Arnd Bergmann <arnd@arndb.de>
|
|
---
|
|
Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt | 2 ++
|
|
1 file changed, 2 insertions(+)
|
|
|
|
diff --git a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt
|
|
index e25436861867..9029b45b8a22 100644
|
|
--- a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt
|
|
+++ b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt
|
|
@@ -18,6 +18,8 @@ Optional properties:
|
|
"ext_clock" (External clock provided to the card).
|
|
- post-power-on-delay-ms : Delay in ms after powering the card and
|
|
de-asserting the reset-gpios (if any)
|
|
+- power-off-delay-us : Delay in us after asserting the reset-gpios (if any)
|
|
+ during power off of the card.
|
|
|
|
Example:
|
|
|
|
|
|
From bc79b1f8ca4d16d45b93c2888474bb3f11b10226 Mon Sep 17 00:00:00 2001
|
|
From: Ulf Hansson <ulf.hansson@linaro.org>
|
|
Date: Sat, 6 May 2017 11:43:05 +0200
|
|
Subject: [PATCH] UPSTREAM: mmc: pwrseq_simple: Parse DTS for the
|
|
power-off-delay-us property
|
|
|
|
If the optional power-off-delay-us property is found, insert the
|
|
corresponding delay after asserting the GPIO during power off. This enables
|
|
a graceful shutdown sequence for some devices.
|
|
|
|
Cc: linux-mmc@vger.kernel.org
|
|
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
|
|
Acked-by: Arnd Bergmann <arnd@arndb.de>
|
|
(cherry picked from commit e9256e142f597edf90c68cec22db4c4aebaa27de)
|
|
---
|
|
drivers/mmc/core/pwrseq_simple.c | 7 +++++++
|
|
1 file changed, 7 insertions(+)
|
|
|
|
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
|
|
index 1304160de168..13ef162cf066 100644
|
|
--- a/drivers/mmc/core/pwrseq_simple.c
|
|
+++ b/drivers/mmc/core/pwrseq_simple.c
|
|
@@ -27,6 +27,7 @@ struct mmc_pwrseq_simple {
|
|
struct mmc_pwrseq pwrseq;
|
|
bool clk_enabled;
|
|
u32 post_power_on_delay_ms;
|
|
+ u32 power_off_delay_us;
|
|
struct clk *ext_clk;
|
|
struct gpio_descs *reset_gpios;
|
|
};
|
|
@@ -78,6 +79,10 @@ static void mmc_pwrseq_simple_power_off(struct mmc_host *host)
|
|
|
|
mmc_pwrseq_simple_set_gpios_value(pwrseq, 1);
|
|
|
|
+ if (pwrseq->power_off_delay_us)
|
|
+ usleep_range(pwrseq->power_off_delay_us,
|
|
+ 2 * pwrseq->power_off_delay_us);
|
|
+
|
|
if (!IS_ERR(pwrseq->ext_clk) && pwrseq->clk_enabled) {
|
|
clk_disable_unprepare(pwrseq->ext_clk);
|
|
pwrseq->clk_enabled = false;
|
|
@@ -119,6 +124,8 @@ static int mmc_pwrseq_simple_probe(struct platform_device *pdev)
|
|
|
|
device_property_read_u32(dev, "post-power-on-delay-ms",
|
|
&pwrseq->post_power_on_delay_ms);
|
|
+ device_property_read_u32(dev, "power-off-delay-us",
|
|
+ &pwrseq->power_off_delay_us);
|
|
|
|
pwrseq->pwrseq.dev = dev;
|
|
pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops;
|