[RFC v2 02/13] pwrseq: port MMC's pwrseq drivers to new pwrseq subsystem

From: Dmitry Baryshkov
Date: Sun Aug 29 2021 - 09:14:46 EST


Port MMC's all pwrseq drivers to new pwrseq subsystem.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@xxxxxxxxxx>
---
.../pwrseq}/mmc-pwrseq-emmc.yaml | 0
.../pwrseq}/mmc-pwrseq-sd8787.yaml | 0
.../pwrseq}/mmc-pwrseq-simple.yaml | 0
drivers/mmc/core/Kconfig | 32 ----
drivers/mmc/core/Makefile | 3 -
drivers/mmc/core/pwrseq_emmc.c | 120 -------------
drivers/mmc/core/pwrseq_sd8787.c | 107 ------------
drivers/mmc/core/pwrseq_simple.c | 164 ------------------
drivers/power/pwrseq/Kconfig | 32 ++++
drivers/power/pwrseq/Makefile | 4 +
drivers/power/pwrseq/pwrseq_emmc.c | 119 +++++++++++++
drivers/power/pwrseq/pwrseq_sd8787.c | 98 +++++++++++
drivers/power/pwrseq/pwrseq_simple.c | 160 +++++++++++++++++
13 files changed, 413 insertions(+), 426 deletions(-)
rename Documentation/devicetree/bindings/{mmc => power/pwrseq}/mmc-pwrseq-emmc.yaml (100%)
rename Documentation/devicetree/bindings/{mmc => power/pwrseq}/mmc-pwrseq-sd8787.yaml (100%)
rename Documentation/devicetree/bindings/{mmc => power/pwrseq}/mmc-pwrseq-simple.yaml (100%)
delete mode 100644 drivers/mmc/core/pwrseq_emmc.c
delete mode 100644 drivers/mmc/core/pwrseq_sd8787.c
delete mode 100644 drivers/mmc/core/pwrseq_simple.c
create mode 100644 drivers/power/pwrseq/pwrseq_emmc.c
create mode 100644 drivers/power/pwrseq/pwrseq_sd8787.c
create mode 100644 drivers/power/pwrseq/pwrseq_simple.c

diff --git a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-emmc.yaml b/Documentation/devicetree/bindings/power/pwrseq/mmc-pwrseq-emmc.yaml
similarity index 100%
rename from Documentation/devicetree/bindings/mmc/mmc-pwrseq-emmc.yaml
rename to Documentation/devicetree/bindings/power/pwrseq/mmc-pwrseq-emmc.yaml
diff --git a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-sd8787.yaml b/Documentation/devicetree/bindings/power/pwrseq/mmc-pwrseq-sd8787.yaml
similarity index 100%
rename from Documentation/devicetree/bindings/mmc/mmc-pwrseq-sd8787.yaml
rename to Documentation/devicetree/bindings/power/pwrseq/mmc-pwrseq-sd8787.yaml
diff --git a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.yaml b/Documentation/devicetree/bindings/power/pwrseq/mmc-pwrseq-simple.yaml
similarity index 100%
rename from Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.yaml
rename to Documentation/devicetree/bindings/power/pwrseq/mmc-pwrseq-simple.yaml
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig
index ae8b69aee619..cf7df64ce009 100644
--- a/drivers/mmc/core/Kconfig
+++ b/drivers/mmc/core/Kconfig
@@ -2,38 +2,6 @@
#
# MMC core configuration
#
-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_SD8787
- tristate "HW reset support for SD8787 BT + Wifi module"
- depends on OF && (MWIFIEX || BT_MRVL_SDIO || LIBERTAS_SDIO)
- help
- This selects hardware reset support for the SD8787 BT + Wifi
- module. By default this option is set to n.
-
- This driver can also be built as a module. If so, the module
- will be called pwrseq_sd8787.
-
-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.
-
config MMC_BLOCK
tristate "MMC block device driver"
depends on BLOCK
diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile
index 6a907736cd7a..322eb69bd00e 100644
--- a/drivers/mmc/core/Makefile
+++ b/drivers/mmc/core/Makefile
@@ -10,9 +10,6 @@ mmc_core-y := core.o bus.o host.o \
sdio_cis.o sdio_io.o sdio_irq.o \
slot-gpio.o regulator.o
mmc_core-$(CONFIG_OF) += pwrseq.o
-obj-$(CONFIG_PWRSEQ_SIMPLE) += pwrseq_simple.o
-obj-$(CONFIG_PWRSEQ_SD8787) += pwrseq_sd8787.o
-obj-$(CONFIG_PWRSEQ_EMMC) += pwrseq_emmc.o
mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o
obj-$(CONFIG_MMC_BLOCK) += mmc_block.o
mmc_block-objs := block.o queue.o
diff --git a/drivers/mmc/core/pwrseq_emmc.c b/drivers/mmc/core/pwrseq_emmc.c
deleted file mode 100644
index f6dde9edd7a3..000000000000
--- a/drivers/mmc/core/pwrseq_emmc.c
+++ /dev/null
@@ -1,120 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) 2015, Samsung Electronics Co., Ltd.
- *
- * Author: Marek Szyprowski <m.szyprowski@xxxxxxxxxxx>
- *
- * Simple eMMC hardware reset provider
- */
-#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>
-#include <linux/gpio/consumer.h>
-#include <linux/reboot.h>
-
-#include <linux/mmc/host.h>
-
-#include "pwrseq.h"
-
-struct mmc_pwrseq_emmc {
- struct mmc_pwrseq pwrseq;
- struct notifier_block reset_nb;
- 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_host *host)
-{
- struct mmc_pwrseq_emmc *pwrseq = to_pwrseq_emmc(host->pwrseq);
-
- gpiod_set_value_cansleep(pwrseq->reset_gpio, 1);
- udelay(1);
- gpiod_set_value_cansleep(pwrseq->reset_gpio, 0);
- udelay(200);
-}
-
-static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this,
- unsigned long mode, void *cmd)
-{
- struct mmc_pwrseq_emmc *pwrseq = container_of(this,
- struct mmc_pwrseq_emmc, reset_nb);
- gpiod_set_value(pwrseq->reset_gpio, 1);
- udelay(1);
- gpiod_set_value(pwrseq->reset_gpio, 0);
- udelay(200);
-
- return NOTIFY_DONE;
-}
-
-static const struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = {
- .reset = mmc_pwrseq_emmc_reset,
-};
-
-static int mmc_pwrseq_emmc_probe(struct platform_device *pdev)
-{
- struct mmc_pwrseq_emmc *pwrseq;
- struct device *dev = &pdev->dev;
-
- pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL);
- if (!pwrseq)
- return -ENOMEM;
-
- pwrseq->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
- if (IS_ERR(pwrseq->reset_gpio))
- return PTR_ERR(pwrseq->reset_gpio);
-
- if (!gpiod_cansleep(pwrseq->reset_gpio)) {
- /*
- * register reset handler to ensure emmc reset also from
- * emergency_reboot(), priority 255 is the highest priority
- * so it will be executed before any system reboot handler.
- */
- pwrseq->reset_nb.notifier_call = mmc_pwrseq_emmc_reset_nb;
- pwrseq->reset_nb.priority = 255;
- register_restart_handler(&pwrseq->reset_nb);
- } else {
- dev_notice(dev, "EMMC reset pin tied to a sleepy GPIO driver; reset on emergency-reboot disabled\n");
- }
-
- 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 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_sd8787.c b/drivers/mmc/core/pwrseq_sd8787.c
deleted file mode 100644
index 68a826f1c0a1..000000000000
--- a/drivers/mmc/core/pwrseq_sd8787.c
+++ /dev/null
@@ -1,107 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * pwrseq_sd8787.c - power sequence support for Marvell SD8787 BT + Wifi chip
- *
- * Copyright (C) 2016 Matt Ranostay <matt@ranostay.consulting>
- *
- * Based on the original work pwrseq_simple.c
- * Copyright (C) 2014 Linaro Ltd
- * Author: Ulf Hansson <ulf.hansson@xxxxxxxxxx>
- */
-
-#include <linux/delay.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>
-#include <linux/gpio/consumer.h>
-
-#include <linux/mmc/host.h>
-
-#include "pwrseq.h"
-
-struct mmc_pwrseq_sd8787 {
- struct mmc_pwrseq pwrseq;
- struct gpio_desc *reset_gpio;
- struct gpio_desc *pwrdn_gpio;
-};
-
-#define to_pwrseq_sd8787(p) container_of(p, struct mmc_pwrseq_sd8787, pwrseq)
-
-static void mmc_pwrseq_sd8787_pre_power_on(struct mmc_host *host)
-{
- struct mmc_pwrseq_sd8787 *pwrseq = to_pwrseq_sd8787(host->pwrseq);
-
- gpiod_set_value_cansleep(pwrseq->reset_gpio, 1);
-
- msleep(300);
- gpiod_set_value_cansleep(pwrseq->pwrdn_gpio, 1);
-}
-
-static void mmc_pwrseq_sd8787_power_off(struct mmc_host *host)
-{
- struct mmc_pwrseq_sd8787 *pwrseq = to_pwrseq_sd8787(host->pwrseq);
-
- gpiod_set_value_cansleep(pwrseq->pwrdn_gpio, 0);
- gpiod_set_value_cansleep(pwrseq->reset_gpio, 0);
-}
-
-static const struct mmc_pwrseq_ops mmc_pwrseq_sd8787_ops = {
- .pre_power_on = mmc_pwrseq_sd8787_pre_power_on,
- .power_off = mmc_pwrseq_sd8787_power_off,
-};
-
-static const struct of_device_id mmc_pwrseq_sd8787_of_match[] = {
- { .compatible = "mmc-pwrseq-sd8787",},
- {/* sentinel */},
-};
-MODULE_DEVICE_TABLE(of, mmc_pwrseq_sd8787_of_match);
-
-static int mmc_pwrseq_sd8787_probe(struct platform_device *pdev)
-{
- struct mmc_pwrseq_sd8787 *pwrseq;
- struct device *dev = &pdev->dev;
-
- pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL);
- if (!pwrseq)
- return -ENOMEM;
-
- pwrseq->pwrdn_gpio = devm_gpiod_get(dev, "powerdown", GPIOD_OUT_LOW);
- if (IS_ERR(pwrseq->pwrdn_gpio))
- return PTR_ERR(pwrseq->pwrdn_gpio);
-
- pwrseq->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
- if (IS_ERR(pwrseq->reset_gpio))
- return PTR_ERR(pwrseq->reset_gpio);
-
- pwrseq->pwrseq.dev = dev;
- pwrseq->pwrseq.ops = &mmc_pwrseq_sd8787_ops;
- pwrseq->pwrseq.owner = THIS_MODULE;
- platform_set_drvdata(pdev, pwrseq);
-
- return mmc_pwrseq_register(&pwrseq->pwrseq);
-}
-
-static int mmc_pwrseq_sd8787_remove(struct platform_device *pdev)
-{
- struct mmc_pwrseq_sd8787 *pwrseq = platform_get_drvdata(pdev);
-
- mmc_pwrseq_unregister(&pwrseq->pwrseq);
-
- return 0;
-}
-
-static struct platform_driver mmc_pwrseq_sd8787_driver = {
- .probe = mmc_pwrseq_sd8787_probe,
- .remove = mmc_pwrseq_sd8787_remove,
- .driver = {
- .name = "pwrseq_sd8787",
- .of_match_table = mmc_pwrseq_sd8787_of_match,
- },
-};
-
-module_platform_driver(mmc_pwrseq_sd8787_driver);
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
deleted file mode 100644
index ea4d3670560e..000000000000
--- a/drivers/mmc/core/pwrseq_simple.c
+++ /dev/null
@@ -1,164 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) 2014 Linaro Ltd
- *
- * Author: Ulf Hansson <ulf.hansson@xxxxxxxxxx>
- *
- * 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>
-#include <linux/gpio/consumer.h>
-#include <linux/delay.h>
-#include <linux/property.h>
-
-#include <linux/mmc/host.h>
-
-#include "pwrseq.h"
-
-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;
-};
-
-#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)
-{
- struct gpio_descs *reset_gpios = pwrseq->reset_gpios;
-
- if (!IS_ERR(reset_gpios)) {
- unsigned long *values;
- int nvalues = reset_gpios->ndescs;
-
- values = bitmap_alloc(nvalues, GFP_KERNEL);
- if (!values)
- return;
-
- if (value)
- bitmap_fill(values, nvalues);
- else
- bitmap_zero(values, nvalues);
-
- gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
- reset_gpios->info, values);
-
- kfree(values);
- }
-}
-
-static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host)
-{
- 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);
- pwrseq->clk_enabled = true;
- }
-
- mmc_pwrseq_simple_set_gpios_value(pwrseq, 1);
-}
-
-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)
-{
- struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq);
-
- 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;
- }
-}
-
-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,
-};
-
-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;
- struct device *dev = &pdev->dev;
-
- pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL);
- if (!pwrseq)
- return -ENOMEM;
-
- 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 = 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) {
- return PTR_ERR(pwrseq->reset_gpios);
- }
-
- 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;
- pwrseq->pwrseq.owner = THIS_MODULE;
- platform_set_drvdata(pdev, pwrseq);
-
- 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");
diff --git a/drivers/power/pwrseq/Kconfig b/drivers/power/pwrseq/Kconfig
index 8904ec9ed541..36339a456b03 100644
--- a/drivers/power/pwrseq/Kconfig
+++ b/drivers/power/pwrseq/Kconfig
@@ -8,4 +8,36 @@ menuconfig PWRSEQ

if PWRSEQ

+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_SD8787
+ tristate "HW reset support for SD8787 BT + Wifi module"
+ depends on OF
+ help
+ This selects hardware reset support for the SD8787 BT + Wifi
+ module. By default this option is set to n.
+
+ This driver can also be built as a module. If so, the module
+ will be called pwrseq_sd8787.
+
+config PWRSEQ_SIMPLE
+ tristate "Simple HW reset support"
+ default y
+ depends on OF
+ help
+ This selects simple hardware reset support aka pwrseq-simple.
+ 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.
+
endif
diff --git a/drivers/power/pwrseq/Makefile b/drivers/power/pwrseq/Makefile
index 108429ff6445..6f359d228843 100644
--- a/drivers/power/pwrseq/Makefile
+++ b/drivers/power/pwrseq/Makefile
@@ -4,3 +4,7 @@
#

obj-$(CONFIG_PWRSEQ) += core.o
+
+obj-$(CONFIG_PWRSEQ_EMMC) += pwrseq_emmc.o
+obj-$(CONFIG_PWRSEQ_SD8787) += pwrseq_sd8787.o
+obj-$(CONFIG_PWRSEQ_SIMPLE) += pwrseq_simple.o
diff --git a/drivers/power/pwrseq/pwrseq_emmc.c b/drivers/power/pwrseq/pwrseq_emmc.c
new file mode 100644
index 000000000000..4a2da837efd0
--- /dev/null
+++ b/drivers/power/pwrseq/pwrseq_emmc.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2015, Samsung Electronics Co., Ltd.
+ *
+ * Author: Marek Szyprowski <m.szyprowski@xxxxxxxxxxx>
+ *
+ * Simple eMMC hardware reset provider
+ */
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/reboot.h>
+#include <linux/pwrseq/driver.h>
+
+struct pwrseq_emmc {
+ struct notifier_block reset_nb;
+ struct gpio_desc *reset_gpio;
+};
+
+static void pwrseq_ereset(struct pwrseq *pwrseq)
+{
+ struct pwrseq_emmc *pwrseq_emmc = pwrseq_get_data(pwrseq);
+
+ gpiod_set_value_cansleep(pwrseq_emmc->reset_gpio, 1);
+ udelay(1);
+ gpiod_set_value_cansleep(pwrseq_emmc->reset_gpio, 0);
+ udelay(200);
+}
+
+static int pwrseq_ereset_nb(struct notifier_block *this,
+ unsigned long mode, void *cmd)
+{
+ struct pwrseq_emmc *pwrseq_emmc = container_of(this,
+ struct pwrseq_emmc, reset_nb);
+ gpiod_set_value(pwrseq_emmc->reset_gpio, 1);
+ udelay(1);
+ gpiod_set_value(pwrseq_emmc->reset_gpio, 0);
+ udelay(200);
+
+ return NOTIFY_DONE;
+}
+
+static const struct pwrseq_ops pwrseq_eops = {
+ .reset = pwrseq_ereset,
+};
+
+static int pwrseq_eprobe(struct platform_device *pdev)
+{
+ struct pwrseq_emmc *pwrseq_emmc;
+ struct pwrseq *pwrseq;
+ struct pwrseq_provider *provider;
+ struct device *dev = &pdev->dev;
+
+ pwrseq_emmc = devm_kzalloc(dev, sizeof(*pwrseq_emmc), GFP_KERNEL);
+ if (!pwrseq_emmc)
+ return -ENOMEM;
+
+ pwrseq_emmc->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(pwrseq_emmc->reset_gpio))
+ return PTR_ERR(pwrseq_emmc->reset_gpio);
+
+ if (!gpiod_cansleep(pwrseq_emmc->reset_gpio)) {
+ /*
+ * register reset handler to ensure emmc reset also from
+ * emergency_reboot(), priority 255 is the highest priority
+ * so it will be executed before any system reboot handler.
+ */
+ pwrseq_emmc->reset_nb.notifier_call = pwrseq_ereset_nb;
+ pwrseq_emmc->reset_nb.priority = 255;
+ register_restart_handler(&pwrseq_emmc->reset_nb);
+ } else {
+ dev_notice(dev, "EMMC reset pin tied to a sleepy GPIO driver; reset on emergency-reboot disabled\n");
+ }
+
+ platform_set_drvdata(pdev, pwrseq_emmc);
+
+ pwrseq = devm_pwrseq_create(dev, &pwrseq_eops, pwrseq_emmc);
+ if (IS_ERR(pwrseq))
+ return PTR_ERR(pwrseq);
+
+ provider = devm_of_pwrseq_provider_register(dev, of_pwrseq_xlate_single, pwrseq);
+
+ return PTR_ERR_OR_ZERO(provider);
+}
+
+static int pwrseq_eremove(struct platform_device *pdev)
+{
+ struct pwrseq_emmc *pwrseq_emmc = platform_get_drvdata(pdev);
+
+ unregister_restart_handler(&pwrseq_emmc->reset_nb);
+
+ return 0;
+}
+
+static const struct of_device_id pwrseq_eof_match[] = {
+ { .compatible = "mmc-pwrseq-emmc",},
+ {/* sentinel */},
+};
+
+MODULE_DEVICE_TABLE(of, pwrseq_eof_match);
+
+static struct platform_driver pwrseq_edriver = {
+ .probe = pwrseq_eprobe,
+ .remove = pwrseq_eremove,
+ .driver = {
+ .name = "pwrseq_emmc",
+ .of_match_table = pwrseq_eof_match,
+ },
+};
+
+module_platform_driver(pwrseq_edriver);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/pwrseq/pwrseq_sd8787.c b/drivers/power/pwrseq/pwrseq_sd8787.c
new file mode 100644
index 000000000000..c5d6c639380f
--- /dev/null
+++ b/drivers/power/pwrseq/pwrseq_sd8787.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * pwrseq_sd8787.c - power sequence support for Marvell SD8787 BT + Wifi chip
+ *
+ * Copyright (C) 2016 Matt Ranostay <matt@ranostay.consulting>
+ *
+ * Based on the original work pwrseq_sd8787.c
+ * Copyright (C) 2014 Linaro Ltd
+ * Author: Ulf Hansson <ulf.hansson@xxxxxxxxxx>
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+
+#include <linux/pwrseq/driver.h>
+
+struct pwrseq_sd8787 {
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *pwrdn_gpio;
+};
+
+static int pwrseq_sd8787_pre_power_on(struct pwrseq *pwrseq)
+{
+ struct pwrseq_sd8787 *pwrseq_sd8787 = pwrseq_get_data(pwrseq);
+
+ gpiod_set_value_cansleep(pwrseq_sd8787->reset_gpio, 1);
+
+ msleep(300);
+ gpiod_set_value_cansleep(pwrseq_sd8787->pwrdn_gpio, 1);
+
+ return 0;
+}
+
+static void pwrseq_sd8787_power_off(struct pwrseq *pwrseq)
+{
+ struct pwrseq_sd8787 *pwrseq_sd8787 = pwrseq_get_data(pwrseq);
+
+ gpiod_set_value_cansleep(pwrseq_sd8787->pwrdn_gpio, 0);
+ gpiod_set_value_cansleep(pwrseq_sd8787->reset_gpio, 0);
+}
+
+static const struct pwrseq_ops pwrseq_sd8787_ops = {
+ .pre_power_on = pwrseq_sd8787_pre_power_on,
+ .power_off = pwrseq_sd8787_power_off,
+};
+
+static const struct of_device_id pwrseq_sd8787_of_match[] = {
+ { .compatible = "mmc-pwrseq-sd8787",},
+ {/* sentinel */},
+};
+MODULE_DEVICE_TABLE(of, pwrseq_sd8787_of_match);
+
+static int pwrseq_sd8787_probe(struct platform_device *pdev)
+{
+ struct pwrseq_sd8787 *pwrseq_sd8787;
+ struct pwrseq *pwrseq;
+ struct pwrseq_provider *provider;
+ struct device *dev = &pdev->dev;
+
+ pwrseq_sd8787 = devm_kzalloc(dev, sizeof(*pwrseq_sd8787), GFP_KERNEL);
+ if (!pwrseq_sd8787)
+ return -ENOMEM;
+
+ pwrseq_sd8787->pwrdn_gpio = devm_gpiod_get(dev, "powerdown", GPIOD_OUT_LOW);
+ if (IS_ERR(pwrseq_sd8787->pwrdn_gpio))
+ return PTR_ERR(pwrseq_sd8787->pwrdn_gpio);
+
+ pwrseq_sd8787->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(pwrseq_sd8787->reset_gpio))
+ return PTR_ERR(pwrseq_sd8787->reset_gpio);
+
+ pwrseq = devm_pwrseq_create(dev, &pwrseq_sd8787_ops, pwrseq_sd8787);
+ if (IS_ERR(pwrseq))
+ return PTR_ERR(pwrseq);
+
+ provider = devm_of_pwrseq_provider_register(dev, of_pwrseq_xlate_single, pwrseq);
+
+ return PTR_ERR_OR_ZERO(provider);
+}
+
+static struct platform_driver pwrseq_sd8787_driver = {
+ .probe = pwrseq_sd8787_probe,
+ .driver = {
+ .name = "pwrseq_sd8787",
+ .of_match_table = pwrseq_sd8787_of_match,
+ },
+};
+
+module_platform_driver(pwrseq_sd8787_driver);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/pwrseq/pwrseq_simple.c b/drivers/power/pwrseq/pwrseq_simple.c
new file mode 100644
index 000000000000..f8d6e6e077df
--- /dev/null
+++ b/drivers/power/pwrseq/pwrseq_simple.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2014 Linaro Ltd
+ *
+ * Author: Ulf Hansson <ulf.hansson@xxxxxxxxxx>
+ *
+ * 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/mod_devicetable.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/delay.h>
+#include <linux/property.h>
+#include <linux/pwrseq/driver.h>
+
+struct pwrseq_simple {
+ bool clk_enabled;
+ u32 post_power_on_delay_ms;
+ u32 power_off_delay_us;
+ struct clk *ext_clk;
+ struct gpio_descs *reset_gpios;
+};
+
+static int pwrseq_simple_set_gpios_value(struct pwrseq_simple *pwrseq_simple,
+ int value)
+{
+ struct gpio_descs *reset_gpios = pwrseq_simple->reset_gpios;
+ unsigned long *values;
+ int nvalues;
+ int ret;
+
+ if (IS_ERR(reset_gpios))
+ return PTR_ERR(reset_gpios);
+
+ nvalues = reset_gpios->ndescs;
+
+ values = bitmap_alloc(nvalues, GFP_KERNEL);
+ if (!values)
+ return -ENOMEM;
+
+ if (value)
+ bitmap_fill(values, nvalues);
+ else
+ bitmap_zero(values, nvalues);
+
+ ret = gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
+ reset_gpios->info, values);
+ kfree(values);
+
+ return ret;
+}
+
+static int pwrseq_simple_pre_power_on(struct pwrseq *pwrseq)
+{
+ struct pwrseq_simple *pwrseq_simple = pwrseq_get_data(pwrseq);
+
+ if (!IS_ERR(pwrseq_simple->ext_clk) && !pwrseq_simple->clk_enabled) {
+ clk_prepare_enable(pwrseq_simple->ext_clk);
+ pwrseq_simple->clk_enabled = true;
+ }
+
+ return pwrseq_simple_set_gpios_value(pwrseq_simple, 1);
+}
+
+static int pwrseq_simple_power_on(struct pwrseq *pwrseq)
+{
+ struct pwrseq_simple *pwrseq_simple = pwrseq_get_data(pwrseq);
+ int ret;
+
+ ret = pwrseq_simple_set_gpios_value(pwrseq_simple, 0);
+ if (ret)
+ return ret;
+
+ if (pwrseq_simple->post_power_on_delay_ms)
+ msleep(pwrseq_simple->post_power_on_delay_ms);
+
+ return 0;
+}
+
+static void pwrseq_simple_power_off(struct pwrseq *pwrseq)
+{
+ struct pwrseq_simple *pwrseq_simple = pwrseq_get_data(pwrseq);
+
+ pwrseq_simple_set_gpios_value(pwrseq_simple, 1);
+
+ if (pwrseq_simple->power_off_delay_us)
+ usleep_range(pwrseq_simple->power_off_delay_us,
+ 2 * pwrseq_simple->power_off_delay_us);
+
+ if (!IS_ERR(pwrseq_simple->ext_clk) && pwrseq_simple->clk_enabled) {
+ clk_disable_unprepare(pwrseq_simple->ext_clk);
+ pwrseq_simple->clk_enabled = false;
+ }
+}
+
+static const struct pwrseq_ops pwrseq_simple_ops = {
+ .pre_power_on = pwrseq_simple_pre_power_on,
+ .power_on = pwrseq_simple_power_on,
+ .power_off = pwrseq_simple_power_off,
+};
+
+static const struct of_device_id pwrseq_simple_of_match[] = {
+ { .compatible = "mmc-pwrseq-simple",}, /* MMC-specific compatible */
+ {/* sentinel */},
+};
+MODULE_DEVICE_TABLE(of, pwrseq_simple_of_match);
+
+static int pwrseq_simple_probe(struct platform_device *pdev)
+{
+ struct pwrseq_simple *pwrseq_simple;
+ struct pwrseq *pwrseq;
+ struct pwrseq_provider *provider;
+ struct device *dev = &pdev->dev;
+
+ pwrseq_simple = devm_kzalloc(dev, sizeof(*pwrseq_simple), GFP_KERNEL);
+ if (!pwrseq_simple)
+ return -ENOMEM;
+
+ pwrseq_simple->ext_clk = devm_clk_get(dev, "ext_clock");
+ if (IS_ERR(pwrseq_simple->ext_clk) && PTR_ERR(pwrseq_simple->ext_clk) != -ENOENT)
+ return PTR_ERR(pwrseq_simple->ext_clk);
+
+ pwrseq_simple->reset_gpios = devm_gpiod_get_array(dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(pwrseq_simple->reset_gpios) &&
+ PTR_ERR(pwrseq_simple->reset_gpios) != -ENOENT &&
+ PTR_ERR(pwrseq_simple->reset_gpios) != -ENOSYS) {
+ return PTR_ERR(pwrseq_simple->reset_gpios);
+ }
+
+ device_property_read_u32(dev, "post-power-on-delay-ms",
+ &pwrseq_simple->post_power_on_delay_ms);
+ device_property_read_u32(dev, "power-off-delay-us",
+ &pwrseq_simple->power_off_delay_us);
+
+ pwrseq = devm_pwrseq_create(dev, &pwrseq_simple_ops, pwrseq_simple);
+ if (IS_ERR(pwrseq))
+ return PTR_ERR(pwrseq);
+
+ provider = devm_of_pwrseq_provider_register(dev, of_pwrseq_xlate_single, pwrseq);
+
+ return PTR_ERR_OR_ZERO(provider);
+}
+
+static struct platform_driver pwrseq_simple_driver = {
+ .probe = pwrseq_simple_probe,
+ .driver = {
+ .name = "pwrseq_simple",
+ .of_match_table = pwrseq_simple_of_match,
+ },
+};
+
+module_platform_driver(pwrseq_simple_driver);
+MODULE_LICENSE("GPL v2");
--
2.33.0