[PATCH 00/12] power: add generic power sequence framework

STALE3734d

13 messages, 5 authors, 2016-06-21 · open the first message on its own page

[PATCH 00/12] power: add generic power sequence framework

From: Peter Chen <hidden>
Date: 2016-06-17 10:09:13

Hi all,

We have an well-known problem that the device needs to do some power
sequence before it can be recognized by related host, the typical
example like hard-wired mmc devices and usb devices. This power
sequence is hard to be described at device tree and handled by
related host driver, so we have created a common power sequence
framework to handle this requirement. The generic code is supplied
some common helpers from host driver, and individual power sequence
driver handles kinds of power sequence for devices.

Since the MMC has already done the similar things, and this power
sequence handling can be generic, we use mmc power sequence code
as base to create this framework.

This patch set is based on Krzysztof Kozlowski's RFC patch set (v4.7-rc1)
[1], and making some changes which can let it be generic. After that,
we create a generic power sequence driver for USB devices which handles
below things, it includes all input signals for devices I can consider.

- Clock and its frequencies
- GPIO for reset and the duration time
- GPIO for enable
- Regulator for power

This patch set is tested on i.mx6 sabresx evk using a dts change, I use
two hot-plug devices to simulate this use case. The udoo board changes
were tested using my last power sequence patch set. [2]

@Maciej S. Szmigiero, Oscar, would you please test them on imx6qdl udoo
boards?
@Krzysztof Kozlowski, I have not written changes for your case since I
find the other sequences have already at drivers/usb/misc/usb3503.c,
this driver should be the place for your power sequence, I am not
sure if enable regualator at usb core is suitable for you or not.
@others, it is better you can help test this patch set for MMC devices
and your USB devices with dts changes.

Thank you for your time!

[1] http://www.spinics.net/lists/linux-usb/msg142351.html
[2] http://lists.infradead.org/pipermail/linux-arm-kernel/2016-March/413580.html

Krzysztof Kozlowski (5):
  power/mmc: Move pwrseq drivers to power/pwrseq
  MAINTAINERS: Retain Ulf Hansson as the same maintainer of pwrseq
  power: pwrseq: Enable COMPILE_TEST for drivers
  power: pwrseq: Remove mmc prefix from mmc_pwrseq
  power: pwrseq: Generalize mmc_pwrseq operations by removing mmc prefix

Peter Chen (7):
  power: pwrseq: change common helpers as generic
  power: pwrseq: rename file name for generic driver
  doc: binding: pwrseq-usb-generic: add binding doc for generic usb
    power sequence driver
  power: pwrseq: pwrseq_usb_generic: add generic power sequence support
    for USB deivces
  usb: core: add power sequence handling for USB devices
  usb: chipidea: host: let the hcd know's parent device node
  ARM: dts: imx6qdl-udoo.dtsi: fix onboard USB HUB property

 .../{mmc => power/pwrseq}/mmc-pwrseq-simple.txt    |   0
 .../pwrseq/pwrseq-emmc.txt}                        |   0
 .../bindings/power/pwrseq/pwrseq-usb-generic.txt   |  31 ++++
 .../devicetree/bindings/usb/usb-device.txt         |   2 +
 MAINTAINERS                                        |   9 +
 arch/arm/boot/dts/imx6qdl-udoo.dtsi                |  30 +--
 drivers/mmc/Kconfig                                |   2 -
 drivers/mmc/core/Kconfig                           |  24 ---
 drivers/mmc/core/Makefile                          |   3 -
 drivers/mmc/core/core.c                            |   8 +-
 drivers/mmc/core/host.c                            |  10 +-
 drivers/mmc/core/pwrseq.h                          |  52 ------
 drivers/power/Kconfig                              |   1 +
 drivers/power/Makefile                             |   1 +
 drivers/power/pwrseq/Kconfig                       |  43 +++++
 drivers/power/pwrseq/Makefile                      |   4 +
 drivers/{mmc/core/pwrseq.c => power/pwrseq/core.c} |  71 ++++----
 drivers/{mmc/core => power/pwrseq}/pwrseq_emmc.c   |  17 +-
 .../pwrseq/pwrseq_mmc_simple.c}                    |  33 ++--
 drivers/power/pwrseq/pwrseq_usb_generic.c          | 202 +++++++++++++++++++++
 drivers/usb/chipidea/host.c                        |  18 +-
 drivers/usb/core/hub.c                             |  73 +++++++-
 drivers/usb/core/hub.h                             |   1 +
 include/linux/mmc/host.h                           |   4 +-
 include/linux/pwrseq.h                             |  61 +++++++
 25 files changed, 527 insertions(+), 173 deletions(-)
 rename Documentation/devicetree/bindings/{mmc => power/pwrseq}/mmc-pwrseq-simple.txt (100%)
 rename Documentation/devicetree/bindings/{mmc/mmc-pwrseq-emmc.txt => power/pwrseq/pwrseq-emmc.txt} (100%)
 create mode 100644 Documentation/devicetree/bindings/power/pwrseq/pwrseq-usb-generic.txt
 delete mode 100644 drivers/mmc/core/Kconfig
 delete mode 100644 drivers/mmc/core/pwrseq.h
 create mode 100644 drivers/power/pwrseq/Kconfig
 create mode 100644 drivers/power/pwrseq/Makefile
 rename drivers/{mmc/core/pwrseq.c => power/pwrseq/core.c} (51%)
 rename drivers/{mmc/core => power/pwrseq}/pwrseq_emmc.c (88%)
 rename drivers/{mmc/core/pwrseq_simple.c => power/pwrseq/pwrseq_mmc_simple.c} (81%)
 create mode 100644 drivers/power/pwrseq/pwrseq_usb_generic.c
 create mode 100644 include/linux/pwrseq.h

-- 
1.9.1

[PATCH 01/12] power/mmc: Move pwrseq drivers to power/pwrseq

From: Peter Chen <hidden>
Date: 2016-06-17 10:09:14

From: Krzysztof Kozlowski <redacted>

The MMC power sequence drivers are useful also outside of MMC world: for
USB devices needed a hard-reset before probing. Before extending and
re-using pwrseq drivers, move them to a new place.

The commit does not introduce significant changes in the pwrseq drivers
code so still all the functions are prefixed with "mmc_pwrseq". However
the MMC-specific pwrseq functions has to be now exported and everything
is hidden not by CONFIG_OF but by new CONFIG_POWER_SEQ option.

Signed-off-by: Krzysztof Kozlowski <redacted>
Reviewed-by: Javier Martinez Canillas <redacted>
---
 .../pwrseq/pwrseq-emmc.txt}                            |  0
 .../pwrseq/pwrseq-simple.txt}                          |  0
 drivers/mmc/Kconfig                                    |  2 --
 drivers/mmc/core/Makefile                              |  3 ---
 drivers/mmc/core/core.c                                |  2 +-
 drivers/mmc/core/host.c                                |  2 +-
 drivers/power/Kconfig                                  |  1 +
 drivers/power/Makefile                                 |  1 +
 drivers/{mmc/core => power/pwrseq}/Kconfig             | 18 +++++++++++++-----
 drivers/power/pwrseq/Makefile                          |  3 +++
 drivers/{mmc/core => power/pwrseq}/pwrseq.c            |  8 ++++++--
 drivers/{mmc/core => power/pwrseq}/pwrseq_emmc.c       |  3 +--
 drivers/{mmc/core => power/pwrseq}/pwrseq_simple.c     |  3 +--
 {drivers/mmc/core => include/linux}/pwrseq.h           |  6 +++---
 14 files changed, 31 insertions(+), 21 deletions(-)
 rename Documentation/devicetree/bindings/{mmc/mmc-pwrseq-emmc.txt => power/pwrseq/pwrseq-emmc.txt} (100%)
 rename Documentation/devicetree/bindings/{mmc/mmc-pwrseq-simple.txt => power/pwrseq/pwrseq-simple.txt} (100%)
 rename drivers/{mmc/core => power/pwrseq}/Kconfig (66%)
 create mode 100644 drivers/power/pwrseq/Makefile
 rename drivers/{mmc/core => power/pwrseq}/pwrseq.c (90%)
 rename drivers/{mmc/core => power/pwrseq}/pwrseq_emmc.c (99%)
 rename drivers/{mmc/core => power/pwrseq}/pwrseq_simple.c (99%)
 rename {drivers/mmc/core => include/linux}/pwrseq.h (94%)
diff --git a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-emmc.txt b/Documentation/devicetree/bindings/power/pwrseq/pwrseq-emmc.txt
similarity index 100%
rename from Documentation/devicetree/bindings/mmc/mmc-pwrseq-emmc.txt
rename to Documentation/devicetree/bindings/power/pwrseq/pwrseq-emmc.txt
diff --git a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt b/Documentation/devicetree/bindings/power/pwrseq/pwrseq-simple.txt
similarity index 100%
rename from Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt
rename to Documentation/devicetree/bindings/power/pwrseq/pwrseq-simple.txt
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index f2eeb38..7ade379 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -21,8 +21,6 @@ config MMC_DEBUG
 
 if MMC
 
-source "drivers/mmc/core/Kconfig"
-
 source "drivers/mmc/card/Kconfig"
 
 source "drivers/mmc/host/Kconfig"
diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile
index f007151..a901d3c 100644
--- a/drivers/mmc/core/Makefile
+++ b/drivers/mmc/core/Makefile
@@ -8,7 +8,4 @@ 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
-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/core.c b/drivers/mmc/core/core.c
index 8b4dfd4..edefc3f 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -25,6 +25,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/pm_wakeup.h>
 #include <linux/suspend.h>
+#include <linux/pwrseq.h>
 #include <linux/fault-inject.h>
 #include <linux/random.h>
 #include <linux/slab.h>
@@ -43,7 +44,6 @@
 #include "bus.h"
 #include "host.h"
 #include "sdio_bus.h"
-#include "pwrseq.h"
 
 #include "mmc_ops.h"
 #include "sd_ops.h"
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 1be42fa..1db7d58 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -21,6 +21,7 @@
 #include <linux/export.h>
 #include <linux/leds.h>
 #include <linux/slab.h>
+#include <linux/pwrseq.h>
 
 #include <linux/mmc/host.h>
 #include <linux/mmc/card.h>
@@ -29,7 +30,6 @@
 #include "core.h"
 #include "host.h"
 #include "slot-gpio.h"
-#include "pwrseq.h"
 
 #define cls_dev_to_mmc_host(d)	container_of(d, struct mmc_host, class_dev)
 
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 421770d..2702aca 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -511,5 +511,6 @@ config AXP20X_POWER
 
 endif # POWER_SUPPLY
 
+source "drivers/power/pwrseq/Kconfig"
 source "drivers/power/reset/Kconfig"
 source "drivers/power/avs/Kconfig"
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index e46b75d..02f9d5d 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -71,6 +71,7 @@ obj-$(CONFIG_POWER_AVS)		+= avs/
 obj-$(CONFIG_CHARGER_SMB347)	+= smb347-charger.o
 obj-$(CONFIG_CHARGER_TPS65090)	+= tps65090-charger.o
 obj-$(CONFIG_CHARGER_TPS65217)	+= tps65217_charger.o
+obj-$(CONFIG_POWER_SEQ)		+= pwrseq/
 obj-$(CONFIG_POWER_RESET)	+= reset/
 obj-$(CONFIG_AXP288_FUEL_GAUGE) += axp288_fuel_gauge.o
 obj-$(CONFIG_AXP288_CHARGER)	+= axp288_charger.o
diff --git a/drivers/mmc/core/Kconfig b/drivers/power/pwrseq/Kconfig
similarity index 66%
rename from drivers/mmc/core/Kconfig
rename to drivers/power/pwrseq/Kconfig
index 250f223..7ecd66a 100644
--- a/drivers/mmc/core/Kconfig
+++ b/drivers/power/pwrseq/Kconfig
@@ -1,7 +1,13 @@
-#
-# MMC core configuration
-#
-config PWRSEQ_EMMC
+menuconfig POWER_SEQ
+	default y if OF
+	bool "Hardware reset support for specific devices"
+	help
+	  Provides drivers that implements specific power sequences for chips,
+	  using the generic power sequence management interface.
+
+if POWER_SEQ
+
+config POWER_SEQ_EMMC
 	tristate "HW reset support for eMMC"
 	default y
 	depends on OF
@@ -12,7 +18,7 @@ config PWRSEQ_EMMC
 	  This driver can also be built as a module. If so, the module
 	  will be called pwrseq_emmc.
 
-config PWRSEQ_SIMPLE
+config POWER_SEQ_SIMPLE
 	tristate "Simple HW reset support for MMC"
 	default y
 	depends on OF
@@ -22,3 +28,5 @@ config PWRSEQ_SIMPLE
 
 	  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
new file mode 100644
index 0000000..9e40e4b
--- /dev/null
+++ b/drivers/power/pwrseq/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_POWER_SEQ)		+= pwrseq.o
+obj-$(CONFIG_POWER_SEQ_SIMPLE)	+= pwrseq_simple.o
+obj-$(CONFIG_POWER_SEQ_EMMC)	+= pwrseq_emmc.o
diff --git a/drivers/mmc/core/pwrseq.c b/drivers/power/pwrseq/pwrseq.c
similarity index 90%
rename from drivers/mmc/core/pwrseq.c
rename to drivers/power/pwrseq/pwrseq.c
index 9386c47..66310d7 100644
--- a/drivers/mmc/core/pwrseq.c
+++ b/drivers/power/pwrseq/pwrseq.c
@@ -11,11 +11,10 @@
 #include <linux/err.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/pwrseq.h>
 
 #include <linux/mmc/host.h>
 
-#include "pwrseq.h"
-
 static DEFINE_MUTEX(pwrseq_list_mutex);
 static LIST_HEAD(pwrseq_list);
 
@@ -51,6 +50,7 @@ int mmc_pwrseq_alloc(struct mmc_host *host)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(mmc_pwrseq_alloc);
 
 void mmc_pwrseq_pre_power_on(struct mmc_host *host)
 {
@@ -59,6 +59,7 @@ void mmc_pwrseq_pre_power_on(struct mmc_host *host)
 	if (pwrseq && pwrseq->ops->pre_power_on)
 		pwrseq->ops->pre_power_on(host);
 }
+EXPORT_SYMBOL_GPL(mmc_pwrseq_pre_power_on);
 
 void mmc_pwrseq_post_power_on(struct mmc_host *host)
 {
@@ -67,6 +68,7 @@ void mmc_pwrseq_post_power_on(struct mmc_host *host)
 	if (pwrseq && pwrseq->ops->post_power_on)
 		pwrseq->ops->post_power_on(host);
 }
+EXPORT_SYMBOL_GPL(mmc_pwrseq_post_power_on);
 
 void mmc_pwrseq_power_off(struct mmc_host *host)
 {
@@ -75,6 +77,7 @@ void mmc_pwrseq_power_off(struct mmc_host *host)
 	if (pwrseq && pwrseq->ops->power_off)
 		pwrseq->ops->power_off(host);
 }
+EXPORT_SYMBOL_GPL(mmc_pwrseq_power_off);
 
 void mmc_pwrseq_free(struct mmc_host *host)
 {
@@ -85,6 +88,7 @@ void mmc_pwrseq_free(struct mmc_host *host)
 		host->pwrseq = NULL;
 	}
 }
+EXPORT_SYMBOL_GPL(mmc_pwrseq_free);
 
 int mmc_pwrseq_register(struct mmc_pwrseq *pwrseq)
 {
diff --git a/drivers/mmc/core/pwrseq_emmc.c b/drivers/power/pwrseq/pwrseq_emmc.c
similarity index 99%
rename from drivers/mmc/core/pwrseq_emmc.c
rename to drivers/power/pwrseq/pwrseq_emmc.c
index adc9c0c..a0583ed 100644
--- a/drivers/mmc/core/pwrseq_emmc.c
+++ b/drivers/power/pwrseq/pwrseq_emmc.c
@@ -17,11 +17,10 @@
 #include <linux/err.h>
 #include <linux/gpio/consumer.h>
 #include <linux/reboot.h>
+#include <linux/pwrseq.h>
 
 #include <linux/mmc/host.h>
 
-#include "pwrseq.h"
-
 struct mmc_pwrseq_emmc {
 	struct mmc_pwrseq pwrseq;
 	struct notifier_block reset_nb;
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/power/pwrseq/pwrseq_simple.c
similarity index 99%
rename from drivers/mmc/core/pwrseq_simple.c
rename to drivers/power/pwrseq/pwrseq_simple.c
index 450d907..786f1db 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/power/pwrseq/pwrseq_simple.c
@@ -16,11 +16,10 @@
 #include <linux/device.h>
 #include <linux/err.h>
 #include <linux/gpio/consumer.h>
+#include <linux/pwrseq.h>
 
 #include <linux/mmc/host.h>
 
-#include "pwrseq.h"
-
 struct mmc_pwrseq_simple {
 	struct mmc_pwrseq pwrseq;
 	bool clk_enabled;
diff --git a/drivers/mmc/core/pwrseq.h b/include/linux/pwrseq.h
similarity index 94%
rename from drivers/mmc/core/pwrseq.h
rename to include/linux/pwrseq.h
index d69e751..e230670 100644
--- a/drivers/mmc/core/pwrseq.h
+++ b/include/linux/pwrseq.h
@@ -23,7 +23,7 @@ struct mmc_pwrseq {
 	struct module *owner;
 };
 
-#ifdef CONFIG_OF
+#ifdef CONFIG_POWER_SEQ
 
 int mmc_pwrseq_register(struct mmc_pwrseq *pwrseq);
 void mmc_pwrseq_unregister(struct mmc_pwrseq *pwrseq);
@@ -34,7 +34,7 @@ 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);
 
-#else
+#else /* CONFIG_POWER_SEQ */
 
 static inline int mmc_pwrseq_register(struct mmc_pwrseq *pwrseq)
 {
@@ -47,6 +47,6 @@ static inline void mmc_pwrseq_post_power_on(struct mmc_host *host) {}
 static inline void mmc_pwrseq_power_off(struct mmc_host *host) {}
 static inline void mmc_pwrseq_free(struct mmc_host *host) {}
 
-#endif
+#endif /* CONFIG_POWER_SEQ */
 
 #endif
-- 
1.9.1

[PATCH 02/12] MAINTAINERS: Retain Ulf Hansson as the same maintainer of pwrseq

From: Peter Chen <hidden>
Date: 2016-06-17 10:09:15

From: Krzysztof Kozlowski <redacted>

Before moving pwrseq drivers from drivers/mmc/core/ to drivers/power/,
they were maintained by Ulf Hansson.

Signed-off-by: Krzysztof Kozlowski <redacted>
Reviewed-by: Javier Martinez Canillas <redacted>
---
 MAINTAINERS | 9 +++++++++
 1 file changed, 9 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index ed42cb6..0bc1821 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9072,6 +9072,15 @@ F:	include/linux/power_supply.h
 F:	drivers/power/
 X:	drivers/power/avs/
 
+POWER SEQ CORE and DRIVERS
+M:	Ulf Hansson <ulf.hansson@linaro.org>
+L:	linux-mmc at vger.kernel.org
+T:	git git://git.linaro.org/people/ulf.hansson/mmc.git
+S:	Maintained
+F:	drivers/power/pwrseq/
+F:	include/linux/pwrseq.h
+F:	Documentation/devicetree/bindings/power/pwrseq/
+
 POWER STATE COORDINATION INTERFACE (PSCI)
 M:	Mark Rutland <mark.rutland@arm.com>
 M:	Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
-- 
1.9.1

[PATCH 03/12] power: pwrseq: Enable COMPILE_TEST for drivers

From: Peter Chen <hidden>
Date: 2016-06-17 10:09:16

From: Krzysztof Kozlowski <redacted>

Allow build testing for power sequence drivers.

Signed-off-by: Krzysztof Kozlowski <redacted>
Reviewed-by: Javier Martinez Canillas <redacted>
---
 drivers/power/pwrseq/Kconfig | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/power/pwrseq/Kconfig b/drivers/power/pwrseq/Kconfig
index 7ecd66a..c7e9271 100644
--- a/drivers/power/pwrseq/Kconfig
+++ b/drivers/power/pwrseq/Kconfig
@@ -10,7 +10,7 @@ if POWER_SEQ
 config POWER_SEQ_EMMC
 	tristate "HW reset support for eMMC"
 	default y
-	depends on OF
+	depends on OF || COMPILE_TEST
 	help
 	  This selects Hardware reset support aka pwrseq-emmc for eMMC
 	  devices. By default this option is set to y.
@@ -21,7 +21,7 @@ config POWER_SEQ_EMMC
 config POWER_SEQ_SIMPLE
 	tristate "Simple HW reset support for MMC"
 	default y
-	depends on OF
+	depends on OF || COMPILE_TEST
 	help
 	  This selects simple hardware reset support aka pwrseq-simple for MMC
 	  devices. By default this option is set to y.
-- 
1.9.1

[PATCH 05/12] power: pwrseq: Generalize mmc_pwrseq operations by removing mmc prefix

From: Peter Chen <hidden>
Date: 2016-06-17 10:09:18

From: Krzysztof Kozlowski <redacted>

The power sequence hooks (mmc_pwrseq_pre_power_on(),
mmc_pwrseq_post_power_on() and mmc_pwrseq_power_off()) can be made more
generic to allow re-use in other subsystems. They do not need to take
pointer to struct mmc_host but instead the struct pwrseq should be
sufficient.

Remove the "mmc" prefix and use the pointer to struct pwrseq as
argument.

Signed-off-by: Krzysztof Kozlowski <redacted>
Reviewed-by: Javier Martinez Canillas <redacted>
---
 drivers/mmc/core/core.c              |  6 +++---
 drivers/power/pwrseq/pwrseq.c        | 24 +++++++++---------------
 drivers/power/pwrseq/pwrseq_emmc.c   |  6 ++----
 drivers/power/pwrseq/pwrseq_simple.c | 14 ++++++--------
 include/linux/pwrseq.h               | 18 +++++++++---------
 5 files changed, 29 insertions(+), 39 deletions(-)
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index edefc3f..09a4722 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1719,7 +1719,7 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
 	if (host->ios.power_mode == MMC_POWER_ON)
 		return;
 
-	mmc_pwrseq_pre_power_on(host);
+	pwrseq_pre_power_on(host->pwrseq);
 
 	host->ios.vdd = fls(ocr) - 1;
 	host->ios.power_mode = MMC_POWER_UP;
@@ -1740,7 +1740,7 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
 	 */
 	mmc_delay(10);
 
-	mmc_pwrseq_post_power_on(host);
+	pwrseq_post_power_on(host->pwrseq);
 
 	host->ios.clock = host->f_init;
 
@@ -1759,7 +1759,7 @@ void mmc_power_off(struct mmc_host *host)
 	if (host->ios.power_mode == MMC_POWER_OFF)
 		return;
 
-	mmc_pwrseq_power_off(host);
+	pwrseq_power_off(host->pwrseq);
 
 	host->ios.clock = 0;
 	host->ios.vdd = 0;
diff --git a/drivers/power/pwrseq/pwrseq.c b/drivers/power/pwrseq/pwrseq.c
index 9c66582..495a19d 100644
--- a/drivers/power/pwrseq/pwrseq.c
+++ b/drivers/power/pwrseq/pwrseq.c
@@ -52,32 +52,26 @@ int mmc_pwrseq_alloc(struct mmc_host *host)
 }
 EXPORT_SYMBOL_GPL(mmc_pwrseq_alloc);
 
-void mmc_pwrseq_pre_power_on(struct mmc_host *host)
+void pwrseq_pre_power_on(struct pwrseq *pwrseq)
 {
-	struct pwrseq *pwrseq = host->pwrseq;
-
 	if (pwrseq && pwrseq->ops->pre_power_on)
-		pwrseq->ops->pre_power_on(host);
+		pwrseq->ops->pre_power_on(pwrseq);
 }
-EXPORT_SYMBOL_GPL(mmc_pwrseq_pre_power_on);
+EXPORT_SYMBOL_GPL(pwrseq_pre_power_on);
 
-void mmc_pwrseq_post_power_on(struct mmc_host *host)
+void pwrseq_post_power_on(struct pwrseq *pwrseq)
 {
-	struct pwrseq *pwrseq = host->pwrseq;
-
 	if (pwrseq && pwrseq->ops->post_power_on)
-		pwrseq->ops->post_power_on(host);
+		pwrseq->ops->post_power_on(pwrseq);
 }
-EXPORT_SYMBOL_GPL(mmc_pwrseq_post_power_on);
+EXPORT_SYMBOL_GPL(pwrseq_post_power_on);
 
-void mmc_pwrseq_power_off(struct mmc_host *host)
+void pwrseq_power_off(struct pwrseq *pwrseq)
 {
-	struct pwrseq *pwrseq = host->pwrseq;
-
 	if (pwrseq && pwrseq->ops->power_off)
-		pwrseq->ops->power_off(host);
+		pwrseq->ops->power_off(pwrseq);
 }
-EXPORT_SYMBOL_GPL(mmc_pwrseq_power_off);
+EXPORT_SYMBOL_GPL(pwrseq_power_off);
 
 void mmc_pwrseq_free(struct mmc_host *host)
 {
diff --git a/drivers/power/pwrseq/pwrseq_emmc.c b/drivers/power/pwrseq/pwrseq_emmc.c
index a68ac9a..dbb7e75 100644
--- a/drivers/power/pwrseq/pwrseq_emmc.c
+++ b/drivers/power/pwrseq/pwrseq_emmc.c
@@ -19,8 +19,6 @@
 #include <linux/reboot.h>
 #include <linux/pwrseq.h>
 
-#include <linux/mmc/host.h>
-
 struct mmc_pwrseq_emmc {
 	struct pwrseq pwrseq;
 	struct notifier_block reset_nb;
@@ -37,9 +35,9 @@ static void __mmc_pwrseq_emmc_reset(struct mmc_pwrseq_emmc *pwrseq)
 	udelay(200);
 }
 
-static void mmc_pwrseq_emmc_reset(struct mmc_host *host)
+static void mmc_pwrseq_emmc_reset(struct pwrseq *_pwrseq)
 {
-	struct mmc_pwrseq_emmc *pwrseq =  to_pwrseq_emmc(host->pwrseq);
+	struct mmc_pwrseq_emmc *pwrseq = to_pwrseq_emmc(_pwrseq);
 
 	__mmc_pwrseq_emmc_reset(pwrseq);
 }
diff --git a/drivers/power/pwrseq/pwrseq_simple.c b/drivers/power/pwrseq/pwrseq_simple.c
index d5fbd65..93807a6 100644
--- a/drivers/power/pwrseq/pwrseq_simple.c
+++ b/drivers/power/pwrseq/pwrseq_simple.c
@@ -18,8 +18,6 @@
 #include <linux/gpio/consumer.h>
 #include <linux/pwrseq.h>
 
-#include <linux/mmc/host.h>
-
 struct mmc_pwrseq_simple {
 	struct pwrseq pwrseq;
 	bool clk_enabled;
@@ -46,9 +44,9 @@ 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)
+static void mmc_pwrseq_simple_pre_power_on(struct pwrseq *_pwrseq)
 {
-	struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq);
+	struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(_pwrseq);
 
 	if (!IS_ERR(pwrseq->ext_clk) && !pwrseq->clk_enabled) {
 		clk_prepare_enable(pwrseq->ext_clk);
@@ -58,16 +56,16 @@ static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host)
 	mmc_pwrseq_simple_set_gpios_value(pwrseq, 1);
 }
 
-static void mmc_pwrseq_simple_post_power_on(struct mmc_host *host)
+static void mmc_pwrseq_simple_post_power_on(struct pwrseq *_pwrseq)
 {
-	struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq);
+	struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(_pwrseq);
 
 	mmc_pwrseq_simple_set_gpios_value(pwrseq, 0);
 }
 
-static void mmc_pwrseq_simple_power_off(struct mmc_host *host)
+static void mmc_pwrseq_simple_power_off(struct pwrseq *_pwrseq)
 {
-	struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq);
+	struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(_pwrseq);
 
 	mmc_pwrseq_simple_set_gpios_value(pwrseq, 1);
 
diff --git a/include/linux/pwrseq.h b/include/linux/pwrseq.h
index 6d2d2a4..fcc8fd8 100644
--- a/include/linux/pwrseq.h
+++ b/include/linux/pwrseq.h
@@ -11,9 +11,9 @@
 #include <linux/mmc/host.h>
 
 struct 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 (*pre_power_on)(struct pwrseq *pwrseq);
+	void (*post_power_on)(struct pwrseq *pwrseq);
+	void (*power_off)(struct pwrseq *pwrseq);
 };
 
 struct pwrseq {
@@ -28,10 +28,10 @@ struct pwrseq {
 int pwrseq_register(struct pwrseq *pwrseq);
 void pwrseq_unregister(struct pwrseq *pwrseq);
 
+void pwrseq_pre_power_on(struct pwrseq *pwrseq);
+void pwrseq_post_power_on(struct pwrseq *pwrseq);
+void pwrseq_power_off(struct 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);
 
 #else /* CONFIG_POWER_SEQ */
@@ -41,10 +41,10 @@ static inline int pwrseq_register(struct pwrseq *pwrseq)
 	return -ENOSYS;
 }
 static inline void pwrseq_unregister(struct pwrseq *pwrseq) {}
+static inline void pwrseq_pre_power_on(struct pwrseq *pwrseq) {}
+static inline void pwrseq_post_power_on(struct pwrseq *pwrseq) {}
+static inline void pwrseq_power_off(struct 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) {}
-static inline void mmc_pwrseq_power_off(struct mmc_host *host) {}
 static inline void mmc_pwrseq_free(struct mmc_host *host) {}
 
 #endif /* CONFIG_POWER_SEQ */
-- 
1.9.1

[PATCH 06/12] power: pwrseq: change common helpers as generic

From: Peter Chen <hidden>
Date: 2016-06-17 10:09:19

These helpers are only used by mmc now, change them as generic and
do corresponding changes at mmc code.

Meanwhile, change return value of helper pwrseq_pre_power_on since
it may occur error during enable clock/regulator, etc.

Signed-off-by: Peter Chen <redacted>
---
 drivers/mmc/core/host.c              |  8 +++++--
 drivers/power/pwrseq/pwrseq.c        | 45 ++++++++++++++++++------------------
 drivers/power/pwrseq/pwrseq_simple.c | 10 +++++---
 include/linux/pwrseq.h               | 31 ++++++++++++++-----------
 4 files changed, 52 insertions(+), 42 deletions(-)
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 1db7d58..c8ffd9b 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -322,7 +322,11 @@ int mmc_of_parse(struct mmc_host *host)
 		host->dsr_req = 0;
 	}
 
-	return mmc_pwrseq_alloc(host);
+	host->pwrseq = pwrseq_alloc(host->parent->of_node, "mmc-pwrseq");
+	if (IS_ERR(host->pwrseq))
+		return PTR_ERR(host->pwrseq);
+	else
+		return 0;
 }
 
 EXPORT_SYMBOL(mmc_of_parse);
@@ -462,7 +466,7 @@ EXPORT_SYMBOL(mmc_remove_host);
  */
 void mmc_free_host(struct mmc_host *host)
 {
-	mmc_pwrseq_free(host);
+	pwrseq_free(host->pwrseq);
 	put_device(&host->class_dev);
 }
 
diff --git a/drivers/power/pwrseq/pwrseq.c b/drivers/power/pwrseq/pwrseq.c
index 495a19d..25e4dfd 100644
--- a/drivers/power/pwrseq/pwrseq.c
+++ b/drivers/power/pwrseq/pwrseq.c
@@ -5,37 +5,38 @@
  *
  * License terms: GNU General Public License (GPL) version 2
  *
- *  MMC power sequence management
+ * Power sequence management helpers
  */
+
+#include <linux/device.h>
 #include <linux/kernel.h>
 #include <linux/err.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/pwrseq.h>
 
-#include <linux/mmc/host.h>
 
 static DEFINE_MUTEX(pwrseq_list_mutex);
 static LIST_HEAD(pwrseq_list);
 
-int mmc_pwrseq_alloc(struct mmc_host *host)
+struct pwrseq *pwrseq_alloc(const struct device_node *node,
+	const char *phandle_name)
 {
 	struct device_node *np;
-	struct pwrseq *p;
+	struct pwrseq *p, *pwrseq = NULL;
 
-	np = of_parse_phandle(host->parent->of_node, "mmc-pwrseq", 0);
+	np = of_parse_phandle(node, phandle_name, 0);
 	if (!np)
-		return 0;
+		return NULL;
 
 	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,
+				dev_err(p->dev,
 					"increasing module refcount failed\n");
 			else
-				host->pwrseq = p;
-
+				pwrseq = p;
 			break;
 		}
 	}
@@ -43,19 +44,21 @@ int mmc_pwrseq_alloc(struct mmc_host *host)
 	of_node_put(np);
 	mutex_unlock(&pwrseq_list_mutex);
 
-	if (!host->pwrseq)
-		return -EPROBE_DEFER;
+	if (!pwrseq)
+		return ERR_PTR(-EPROBE_DEFER);
 
-	dev_info(host->parent, "allocated mmc-pwrseq\n");
+	dev_info(p->dev, "pwrseq is allocated\n");
 
-	return 0;
+	return pwrseq;
 }
-EXPORT_SYMBOL_GPL(mmc_pwrseq_alloc);
+EXPORT_SYMBOL_GPL(pwrseq_alloc);
 
-void pwrseq_pre_power_on(struct pwrseq *pwrseq)
+int pwrseq_pre_power_on(struct pwrseq *pwrseq)
 {
 	if (pwrseq && pwrseq->ops->pre_power_on)
-		pwrseq->ops->pre_power_on(pwrseq);
+		return pwrseq->ops->pre_power_on(pwrseq);
+	else
+		return 0;
 }
 EXPORT_SYMBOL_GPL(pwrseq_pre_power_on);
 
@@ -73,16 +76,12 @@ void pwrseq_power_off(struct pwrseq *pwrseq)
 }
 EXPORT_SYMBOL_GPL(pwrseq_power_off);
 
-void mmc_pwrseq_free(struct mmc_host *host)
+void pwrseq_free(struct pwrseq *pwrseq)
 {
-	struct pwrseq *pwrseq = host->pwrseq;
-
-	if (pwrseq) {
+	if (pwrseq)
 		module_put(pwrseq->owner);
-		host->pwrseq = NULL;
-	}
 }
-EXPORT_SYMBOL_GPL(mmc_pwrseq_free);
+EXPORT_SYMBOL_GPL(pwrseq_free);
 
 int pwrseq_register(struct pwrseq *pwrseq)
 {
diff --git a/drivers/power/pwrseq/pwrseq_simple.c b/drivers/power/pwrseq/pwrseq_simple.c
index 93807a6..349443f 100644
--- a/drivers/power/pwrseq/pwrseq_simple.c
+++ b/drivers/power/pwrseq/pwrseq_simple.c
@@ -44,16 +44,20 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
 	}
 }
 
-static void mmc_pwrseq_simple_pre_power_on(struct pwrseq *_pwrseq)
+static int mmc_pwrseq_simple_pre_power_on(struct pwrseq *_pwrseq)
 {
 	struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(_pwrseq);
+	int ret = 0;
 
 	if (!IS_ERR(pwrseq->ext_clk) && !pwrseq->clk_enabled) {
-		clk_prepare_enable(pwrseq->ext_clk);
-		pwrseq->clk_enabled = true;
+		ret = clk_prepare_enable(pwrseq->ext_clk);
+		if (!ret)
+			pwrseq->clk_enabled = true;
 	}
 
 	mmc_pwrseq_simple_set_gpios_value(pwrseq, 1);
+
+	return 0;
 }
 
 static void mmc_pwrseq_simple_post_power_on(struct pwrseq *_pwrseq)
diff --git a/include/linux/pwrseq.h b/include/linux/pwrseq.h
index fcc8fd8..f726e7e 100644
--- a/include/linux/pwrseq.h
+++ b/include/linux/pwrseq.h
@@ -8,14 +8,6 @@
 #ifndef _LINUX_PWRSEQ_H
 #define _LINUX_PWRSEQ_H
 
-#include <linux/mmc/host.h>
-
-struct pwrseq_ops {
-	void (*pre_power_on)(struct pwrseq *pwrseq);
-	void (*post_power_on)(struct pwrseq *pwrseq);
-	void (*power_off)(struct pwrseq *pwrseq);
-};
-
 struct pwrseq {
 	const struct pwrseq_ops *ops;
 	struct device *dev;
@@ -23,16 +15,23 @@ struct pwrseq {
 	struct module *owner;
 };
 
+struct pwrseq_ops {
+	int (*pre_power_on)(struct pwrseq *pwrseq);
+	void (*post_power_on)(struct pwrseq *pwrseq);
+	void (*power_off)(struct pwrseq *pwrseq);
+};
+
 #ifdef CONFIG_POWER_SEQ
 
 int pwrseq_register(struct pwrseq *pwrseq);
 void pwrseq_unregister(struct pwrseq *pwrseq);
 
-void pwrseq_pre_power_on(struct pwrseq *pwrseq);
+int pwrseq_pre_power_on(struct pwrseq *pwrseq);
 void pwrseq_post_power_on(struct pwrseq *pwrseq);
 void pwrseq_power_off(struct pwrseq *pwrseq);
-int mmc_pwrseq_alloc(struct mmc_host *host);
-void mmc_pwrseq_free(struct mmc_host *host);
+struct pwrseq *pwrseq_alloc(const struct device_node *node,
+	const char *phandle_name);
+void pwrseq_free(struct pwrseq *pwrseq);
 
 #else /* CONFIG_POWER_SEQ */
 
@@ -41,11 +40,15 @@ static inline int pwrseq_register(struct pwrseq *pwrseq)
 	return -ENOSYS;
 }
 static inline void pwrseq_unregister(struct pwrseq *pwrseq) {}
-static inline void pwrseq_pre_power_on(struct pwrseq *pwrseq) {}
+static inline int pwrseq_pre_power_on(struct pwrseq *pwrseq) {}
 static inline void pwrseq_post_power_on(struct pwrseq *pwrseq) {}
 static inline void pwrseq_power_off(struct pwrseq *pwrseq) {}
-static inline int mmc_pwrseq_alloc(struct mmc_host *host) { return 0; }
-static inline void mmc_pwrseq_free(struct mmc_host *host) {}
+static inline struct pwrseq *pwrseq_alloc(const struct device_node *node,
+	const char *phandle_name)
+{
+	return NULL;
+}
+static inline void pwrseq_free(struct mmc_host *host) {}
 
 #endif /* CONFIG_POWER_SEQ */
 
-- 
1.9.1

[PATCH 07/12] power: pwrseq: rename file name for generic driver

From: Peter Chen <hidden>
Date: 2016-06-17 10:09:20

The individual driver file name is better to contain module name.

Signed-off-by: Peter Chen <redacted>
---
 .../power/pwrseq/{pwrseq-simple.txt => mmc-pwrseq-simple.txt}         | 0
 drivers/power/pwrseq/Makefile                                         | 4 ++--
 drivers/power/pwrseq/{pwrseq.c => core.c}                             | 0
 drivers/power/pwrseq/{pwrseq_simple.c => pwrseq_mmc_simple.c}         | 0
 4 files changed, 2 insertions(+), 2 deletions(-)
 rename Documentation/devicetree/bindings/power/pwrseq/{pwrseq-simple.txt => mmc-pwrseq-simple.txt} (100%)
 rename drivers/power/pwrseq/{pwrseq.c => core.c} (100%)
 rename drivers/power/pwrseq/{pwrseq_simple.c => pwrseq_mmc_simple.c} (100%)
diff --git a/Documentation/devicetree/bindings/power/pwrseq/pwrseq-simple.txt b/Documentation/devicetree/bindings/power/pwrseq/mmc-pwrseq-simple.txt
similarity index 100%
rename from Documentation/devicetree/bindings/power/pwrseq/pwrseq-simple.txt
rename to Documentation/devicetree/bindings/power/pwrseq/mmc-pwrseq-simple.txt
diff --git a/drivers/power/pwrseq/Makefile b/drivers/power/pwrseq/Makefile
index 9e40e4b..d475e01 100644
--- a/drivers/power/pwrseq/Makefile
+++ b/drivers/power/pwrseq/Makefile
@@ -1,3 +1,3 @@
-obj-$(CONFIG_POWER_SEQ)		+= pwrseq.o
-obj-$(CONFIG_POWER_SEQ_SIMPLE)	+= pwrseq_simple.o
+obj-$(CONFIG_POWER_SEQ)		+= core.o
+obj-$(CONFIG_POWER_SEQ_SIMPLE)	+= pwrseq_mmc_simple.o
 obj-$(CONFIG_POWER_SEQ_EMMC)	+= pwrseq_emmc.o
diff --git a/drivers/power/pwrseq/pwrseq.c b/drivers/power/pwrseq/core.c
similarity index 100%
rename from drivers/power/pwrseq/pwrseq.c
rename to drivers/power/pwrseq/core.c
diff --git a/drivers/power/pwrseq/pwrseq_simple.c b/drivers/power/pwrseq/pwrseq_mmc_simple.c
similarity index 100%
rename from drivers/power/pwrseq/pwrseq_simple.c
rename to drivers/power/pwrseq/pwrseq_mmc_simple.c
-- 
1.9.1

[PATCH 09/12] power: pwrseq: pwrseq_usb_generic: add generic power sequence support for USB deivces

From: Peter Chen <hidden>
Date: 2016-06-17 10:09:22

Add driver for generic power sequence support for USB deivces
which handles below things:

- Clock and its frequencies
- GPIO for reset and the duration time
- GPIO for enable
- Regulator for power

Signed-off-by: Peter Chen <redacted>
---
 drivers/power/pwrseq/Kconfig              |  11 ++
 drivers/power/pwrseq/Makefile             |   1 +
 drivers/power/pwrseq/pwrseq_usb_generic.c | 202 ++++++++++++++++++++++++++++++
 3 files changed, 214 insertions(+)
 create mode 100644 drivers/power/pwrseq/pwrseq_usb_generic.c
diff --git a/drivers/power/pwrseq/Kconfig b/drivers/power/pwrseq/Kconfig
index c7e9271..9749c6d 100644
--- a/drivers/power/pwrseq/Kconfig
+++ b/drivers/power/pwrseq/Kconfig
@@ -29,4 +29,15 @@ config POWER_SEQ_SIMPLE
 	  This driver can also be built as a module. If so, the module
 	  will be called pwrseq_simple.
 
+config POWER_SEQ_USB_GENERIC
+	tristate "Generic power sequence support for USB devices"
+	default y
+	depends on OF || COMPILE_TEST
+	help
+	  This selects generic power sequence support for USB 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_usb_generic.
+
 endif
diff --git a/drivers/power/pwrseq/Makefile b/drivers/power/pwrseq/Makefile
index d475e01..230bb45 100644
--- a/drivers/power/pwrseq/Makefile
+++ b/drivers/power/pwrseq/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_POWER_SEQ)		+= core.o
 obj-$(CONFIG_POWER_SEQ_SIMPLE)	+= pwrseq_mmc_simple.o
 obj-$(CONFIG_POWER_SEQ_EMMC)	+= pwrseq_emmc.o
+obj-$(CONFIG_POWER_SEQ_USB_GENERIC)	+= pwrseq_usb_generic.o
diff --git a/drivers/power/pwrseq/pwrseq_usb_generic.c b/drivers/power/pwrseq/pwrseq_usb_generic.c
new file mode 100644
index 0000000..6017636
--- /dev/null
+++ b/drivers/power/pwrseq/pwrseq_usb_generic.c
@@ -0,0 +1,202 @@
+/*
+ * usb_generic.c	The generic power sequence driver for USB device
+ *
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ * Author: Peter Chen <peter.chen@nxp.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2  of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/pwrseq.h>
+
+struct usb_pwrseq_generic {
+	struct pwrseq pwrseq;
+	struct gpio_desc *gpiod_reset;
+	struct gpio_desc *gpiod_enable;
+	struct clk *clk;
+	struct regulator *reg_power;
+};
+
+#define to_pwrseq_generic(p) container_of(p, struct usb_pwrseq_generic, pwrseq)
+
+static void usb_pwrseq_power_off(struct pwrseq *_pwrseq)
+{
+	struct usb_pwrseq_generic *pwrseq = to_pwrseq_generic(_pwrseq);
+
+	if (pwrseq->clk)
+		clk_disable_unprepare(pwrseq->clk);
+
+	if (pwrseq->gpiod_enable)
+		gpiod_set_value(pwrseq->gpiod_enable, 0);
+
+	if (pwrseq->reg_power)
+		regulator_disable(pwrseq->reg_power);
+}
+
+static int usb_pwrseq_power_on(struct pwrseq *_pwrseq)
+{
+	struct usb_pwrseq_generic *pwrseq = to_pwrseq_generic(_pwrseq);
+	struct gpio_desc *gpiod_reset = pwrseq->gpiod_reset;
+	struct gpio_desc *gpiod_enable = pwrseq->gpiod_enable;
+	struct device *dev = _pwrseq->dev;
+	struct device_node *node = dev->of_node;
+	u32 duration_us = 50, clk_rate = 0;
+	int ret;
+
+	if (pwrseq->clk) {
+		ret = clk_prepare_enable(pwrseq->clk);
+		if (ret) {
+			dev_err(dev,
+				"Can't enable external clock: %d\n",
+				ret);
+			return ret;
+		}
+
+		of_property_read_u32(node, "clock-frequency", &clk_rate);
+		if (clk_rate) {
+			ret = clk_set_rate(pwrseq->clk, clk_rate);
+			if (ret) {
+				dev_err(dev, "Error setting clock rate\n");
+				goto disable_clk;
+			}
+		}
+	}
+
+	if (gpiod_reset) {
+		of_property_read_u32(node, "reset-duration-us", &duration_us);
+		gpiod_direction_output(gpiod_reset, 1);
+
+		gpiod_set_value(gpiod_reset, 1);
+		usleep_range(duration_us, duration_us + 10);
+		gpiod_set_value(gpiod_reset, 0);
+	}
+
+	if (gpiod_enable) {
+		gpiod_direction_output(gpiod_enable, 1);
+		gpiod_set_value(gpiod_enable, 1);
+	}
+
+	if (pwrseq->reg_power) {
+		ret = regulator_enable(pwrseq->reg_power);
+		if (ret) {
+			dev_err(dev, "Failed to power regulator, ret=%d\n",
+					ret);
+			goto disable_gpio;
+		}
+	}
+
+	return 0;
+
+disable_gpio:
+	if (gpiod_enable)
+		gpiod_set_value(gpiod_enable, 0);
+
+disable_clk:
+	if (pwrseq->clk)
+		clk_disable_unprepare(pwrseq->clk);
+	return ret;
+}
+
+static const struct pwrseq_ops usb_pwrseq_generic_ops = {
+	.pre_power_on = usb_pwrseq_power_on,
+	.power_off = usb_pwrseq_power_off,
+};
+
+static const struct of_device_id usb_pwrseq_generic_of_match[] = {
+	{ .compatible = "usb-pwrseq-generic",},
+	{/* sentinel */},
+};
+MODULE_DEVICE_TABLE(of, usb_pwrseq_generic_of_match);
+
+static int usb_pwrseq_generic_probe(struct platform_device *pdev)
+{
+	struct usb_pwrseq_generic *pwrseq;
+	struct device *dev = &pdev->dev;
+	struct regulator *reg;
+	int ret;
+
+	pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL);
+	if (!pwrseq)
+		return -ENOMEM;
+
+	pwrseq->clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(pwrseq->clk)) {
+		dev_dbg(dev, "Can't get clock: %ld\n",
+			PTR_ERR(pwrseq->clk));
+		pwrseq->clk = NULL;
+	}
+
+	pwrseq->gpiod_reset = devm_gpiod_get_optional(dev, "reset",
+			GPIOD_ASIS);
+	ret = PTR_ERR_OR_ZERO(pwrseq->gpiod_reset);
+	if (ret) {
+		dev_err(dev, "Failed to get reset gpio, err = %d\n", ret);
+		return ret;
+	}
+
+	pwrseq->gpiod_enable = devm_gpiod_get_optional(dev, "enable",
+			GPIOD_ASIS);
+	ret = PTR_ERR_OR_ZERO(pwrseq->gpiod_enable);
+	if (ret) {
+		dev_err(dev, "Failed to get enable gpio, err = %d\n", ret);
+		return ret;
+	}
+
+	reg = devm_regulator_get(dev, "power");
+	if (PTR_ERR(reg) == -EPROBE_DEFER)
+		return -EPROBE_DEFER;
+	else if (PTR_ERR(reg) == -ENODEV)
+		/* no power regulator is needed */
+		reg = NULL;
+	else if (IS_ERR(reg))
+		return PTR_ERR(reg);
+	pwrseq->reg_power = reg;
+
+	pwrseq->pwrseq.dev = dev;
+	pwrseq->pwrseq.ops = &usb_pwrseq_generic_ops;
+	pwrseq->pwrseq.owner = THIS_MODULE;
+	platform_set_drvdata(pdev, pwrseq);
+
+	return pwrseq_register(&pwrseq->pwrseq);
+}
+
+static int usb_pwrseq_generic_remove(struct platform_device *pdev)
+{
+	struct usb_pwrseq_generic *pwrseq = platform_get_drvdata(pdev);
+
+	pwrseq_unregister(&pwrseq->pwrseq);
+
+	return 0;
+}
+
+static struct platform_driver usb_pwrseq_generic_driver = {
+	.probe = usb_pwrseq_generic_probe,
+	.remove = usb_pwrseq_generic_remove,
+	.driver = {
+		.name = "usb_pwrseq_generic",
+		.of_match_table = usb_pwrseq_generic_of_match,
+	},
+};
+
+module_platform_driver(usb_pwrseq_generic_driver);
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

[PATCH 11/12] usb: chipidea: host: let the hcd know's parent device node

From: Peter Chen <hidden>
Date: 2016-06-17 10:09:24

From: Peter Chen <redacted>

Since the hcd (chipidea core device) has no device node, so
if we want to describe the child node under the hcd, we had
to put it under its parent's node (glue layer device), and
in the code, we need to let the hcd knows glue layer's code,
then the USB core can handle this node.

Signed-off-by: Peter Chen <redacted>
Tested-by: Maciej S. Szmigiero <redacted>
---
 drivers/usb/chipidea/host.c | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c
index 053bac9..55120ef 100644
--- a/drivers/usb/chipidea/host.c
+++ b/drivers/usb/chipidea/host.c
@@ -109,15 +109,25 @@ static int host_start(struct ci_hdrc *ci)
 	struct ehci_hcd *ehci;
 	struct ehci_ci_priv *priv;
 	int ret;
+	struct device *dev = ci->dev;
 
-	if (usb_disabled())
+	if (usb_disabled() || !dev)
 		return -ENODEV;
 
-	hcd = usb_create_hcd(&ci_ehci_hc_driver, ci->dev, dev_name(ci->dev));
+	/*
+	 * USB Core will try to get child node under roothub,
+	 * but chipidea core has no of_node, and the child node
+	 * for controller is located at glue layer's node which
+	 * is chipidea core's parent.
+	 */
+	if (dev->parent && dev->parent->of_node)
+		dev->of_node = dev->parent->of_node;
+
+	hcd = usb_create_hcd(&ci_ehci_hc_driver, dev, dev_name(dev));
 	if (!hcd)
 		return -ENOMEM;
 
-	dev_set_drvdata(ci->dev, ci);
+	dev_set_drvdata(dev, ci);
 	hcd->rsrc_start = ci->hw_bank.phys;
 	hcd->rsrc_len = ci->hw_bank.size;
 	hcd->regs = ci->hw_bank.abs;
@@ -143,7 +153,7 @@ static int host_start(struct ci_hdrc *ci)
 		if (ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON) {
 			ret = regulator_enable(ci->platdata->reg_vbus);
 			if (ret) {
-				dev_err(ci->dev,
+				dev_err(dev,
 				"Failed to enable vbus regulator, ret=%d\n",
 									ret);
 				goto put_hcd;
-- 
1.9.1

Re: [PATCH 00/12] power: add generic power sequence framework

From: Maciej S. Szmigiero <hidden>
Date: 2016-06-17 23:29:05

Hi Peter,

On 17.06.2016 12:09, Peter Chen wrote:
Hi all,

We have an well-known problem that the device needs to do some power
sequence before it can be recognized by related host, the typical
example like hard-wired mmc devices and usb devices. This power
sequence is hard to be described at device tree and handled by
related host driver, so we have created a common power sequence
framework to handle this requirement. The generic code is supplied
some common helpers from host driver, and individual power sequence
driver handles kinds of power sequence for devices.

Since the MMC has already done the similar things, and this power
sequence handling can be generic, we use mmc power sequence code
as base to create this framework.

This patch set is based on Krzysztof Kozlowski's RFC patch set (v4.7-rc1)
[1], and making some changes which can let it be generic. After that,
we create a generic power sequence driver for USB devices which handles
below things, it includes all input signals for devices I can consider.

- Clock and its frequencies
- GPIO for reset and the duration time
- GPIO for enable
- Regulator for power

This patch set is tested on i.mx6 sabresx evk using a dts change, I use
two hot-plug devices to simulate this use case. The udoo board changes
were tested using my last power sequence patch set. [2]

@Maciej S. Szmigiero, Oscar, would you please test them on imx6qdl udoo
boards?
Thanks for the patch set, just tested them on my UDOO Quad board and can
confirm that USB and on-board hub work fine.

Tested-by: Maciej S. Szmigiero <redacted>

Maciej

Re: [PATCH 00/12] power: add generic power sequence framework

From: Peter Chen <hidden>
Date: 2016-06-20 11:27:37

On Sat, Jun 18, 2016 at 01:29:05AM +0200, Maciej S. Szmigiero wrote:
Hi Peter,

On 17.06.2016 12:09, Peter Chen wrote:
quoted
Hi all,

We have an well-known problem that the device needs to do some power
sequence before it can be recognized by related host, the typical
example like hard-wired mmc devices and usb devices. This power
sequence is hard to be described at device tree and handled by
related host driver, so we have created a common power sequence
framework to handle this requirement. The generic code is supplied
some common helpers from host driver, and individual power sequence
driver handles kinds of power sequence for devices.

Since the MMC has already done the similar things, and this power
sequence handling can be generic, we use mmc power sequence code
as base to create this framework.

This patch set is based on Krzysztof Kozlowski's RFC patch set (v4.7-rc1)
[1], and making some changes which can let it be generic. After that,
we create a generic power sequence driver for USB devices which handles
below things, it includes all input signals for devices I can consider.

- Clock and its frequencies
- GPIO for reset and the duration time
- GPIO for enable
- Regulator for power

This patch set is tested on i.mx6 sabresx evk using a dts change, I use
two hot-plug devices to simulate this use case. The udoo board changes
were tested using my last power sequence patch set. [2]

@Maciej S. Szmigiero, Oscar, would you please test them on imx6qdl udoo
boards?
Thanks for the patch set, just tested them on my UDOO Quad board and can
confirm that USB and on-board hub work fine.

Tested-by: Maciej S. Szmigiero <redacted>

Maciej
Many thanks, Maciej.

-- 

Best Regards,
Peter Chen

Re: [PATCH 07/12] power: pwrseq: rename file name for generic driver

From: Krzysztof Kozlowski <hidden>
Date: 2016-06-20 12:48:20

On 06/17/2016 12:09 PM, Peter Chen wrote:
quoted hunk
The individual driver file name is better to contain module name.

Signed-off-by: Peter Chen <redacted>
---
 .../power/pwrseq/{pwrseq-simple.txt => mmc-pwrseq-simple.txt}         | 0
 drivers/power/pwrseq/Makefile                                         | 4 ++--
 drivers/power/pwrseq/{pwrseq.c => core.c}                             | 0
 drivers/power/pwrseq/{pwrseq_simple.c => pwrseq_mmc_simple.c}         | 0
 4 files changed, 2 insertions(+), 2 deletions(-)
 rename Documentation/devicetree/bindings/power/pwrseq/{pwrseq-simple.txt => mmc-pwrseq-simple.txt} (100%)
 rename drivers/power/pwrseq/{pwrseq.c => core.c} (100%)
 rename drivers/power/pwrseq/{pwrseq_simple.c => pwrseq_mmc_simple.c} (100%)
diff --git a/Documentation/devicetree/bindings/power/pwrseq/pwrseq-simple.txt b/Documentation/devicetree/bindings/power/pwrseq/mmc-pwrseq-simple.txt
similarity index 100%
rename from Documentation/devicetree/bindings/power/pwrseq/pwrseq-simple.txt
rename to Documentation/devicetree/bindings/power/pwrseq/mmc-pwrseq-simple.txt
diff --git a/drivers/power/pwrseq/Makefile b/drivers/power/pwrseq/Makefile
index 9e40e4b..d475e01 100644
--- a/drivers/power/pwrseq/Makefile
+++ b/drivers/power/pwrseq/Makefile
@@ -1,3 +1,3 @@
-obj-$(CONFIG_POWER_SEQ)		+= pwrseq.o
-obj-$(CONFIG_POWER_SEQ_SIMPLE)	+= pwrseq_simple.o
+obj-$(CONFIG_POWER_SEQ)		+= core.o
+obj-$(CONFIG_POWER_SEQ_SIMPLE)	+= pwrseq_mmc_simple.o
Although the driver was developed for MMC but it is quite generic (or
rather - mmc independent). The name of the driver is just
"pwrseq_simple" so I think MMC prefix is not needed.

Best regards,
Krzysztof

Re: [PATCH 07/12] power: pwrseq: rename file name for generic driver

From: Peter Chen <hidden>
Date: 2016-06-21 02:19:49

On Mon, Jun 20, 2016 at 02:48:20PM +0200, Krzysztof Kozlowski wrote:
On 06/17/2016 12:09 PM, Peter Chen wrote:
quoted
The individual driver file name is better to contain module name.

Signed-off-by: Peter Chen <redacted>
---
 .../power/pwrseq/{pwrseq-simple.txt => mmc-pwrseq-simple.txt}         | 0
 drivers/power/pwrseq/Makefile                                         | 4 ++--
 drivers/power/pwrseq/{pwrseq.c => core.c}                             | 0
 drivers/power/pwrseq/{pwrseq_simple.c => pwrseq_mmc_simple.c}         | 0
 4 files changed, 2 insertions(+), 2 deletions(-)
 rename Documentation/devicetree/bindings/power/pwrseq/{pwrseq-simple.txt => mmc-pwrseq-simple.txt} (100%)
 rename drivers/power/pwrseq/{pwrseq.c => core.c} (100%)
 rename drivers/power/pwrseq/{pwrseq_simple.c => pwrseq_mmc_simple.c} (100%)
diff --git a/Documentation/devicetree/bindings/power/pwrseq/pwrseq-simple.txt b/Documentation/devicetree/bindings/power/pwrseq/mmc-pwrseq-simple.txt
similarity index 100%
rename from Documentation/devicetree/bindings/power/pwrseq/pwrseq-simple.txt
rename to Documentation/devicetree/bindings/power/pwrseq/mmc-pwrseq-simple.txt
diff --git a/drivers/power/pwrseq/Makefile b/drivers/power/pwrseq/Makefile
index 9e40e4b..d475e01 100644
--- a/drivers/power/pwrseq/Makefile
+++ b/drivers/power/pwrseq/Makefile
@@ -1,3 +1,3 @@
-obj-$(CONFIG_POWER_SEQ)		+= pwrseq.o
-obj-$(CONFIG_POWER_SEQ_SIMPLE)	+= pwrseq_simple.o
+obj-$(CONFIG_POWER_SEQ)		+= core.o
+obj-$(CONFIG_POWER_SEQ_SIMPLE)	+= pwrseq_mmc_simple.o
Although the driver was developed for MMC but it is quite generic (or
rather - mmc independent). The name of the driver is just
"pwrseq_simple" so I think MMC prefix is not needed.
It is may be common for MMC, but not USB, or others. Eg, its reset-gpios
is just like enable-gpios. Some reset chip process needs to toggle gpio.

-- 

Best Regards,
Peter Chen
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help