Merge "msm: msm_bus: Integrate external APIs" into msm-4.8
diff --git a/Documentation/arm/msm/boot.txt b/Documentation/arm/msm/boot.txt
new file mode 100644
index 0000000..1a41cd5
--- /dev/null
+++ b/Documentation/arm/msm/boot.txt
@@ -0,0 +1,23 @@
+Introduction
+=============
+The power management integrated circuit (PMIC) records the reason the
+Application processor was powered on in Shared Memory.
+The hardware and software used is the shared memory interface. This document
+is not for the purpose of describing this interface, but to identify the
+possible values for this data item.
+
+Description
+===========
+Shared memory item (SMEM_POWER_ON_STATUS_INFO) is read to get access to
+this data. The table below identifies the possible values stored.
+
+power_on_status values set by the PMIC for power on event:
+----------------------------------------------------------
+0x01 -- keyboard power on
+0x02 -- RTC alarm
+0x04 -- cable power on
+0x08 -- SMPL
+0x10 -- Watch Dog timeout
+0x20 -- USB charger
+0x40 -- Wall charger
+0xFF -- error reading power_on_status value
diff --git a/Documentation/devicetree/bindings/input/qpnp-power-on.txt b/Documentation/devicetree/bindings/input/qpnp-power-on.txt
new file mode 100644
index 0000000..a596aa1
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/qpnp-power-on.txt
@@ -0,0 +1,199 @@
+Qualcomm QPNP power-on
+
+The qpnp-power-on is a driver which supports the power-on(PON)
+peripheral on Qualcomm PMICs. The supported functionality includes
+power on/off reason, key press/release detection, PMIC reset configurations
+and other PON specific features. The PON module supports multiple physical
+power-on (KPDPWR_N, CBLPWR) and reset (KPDPWR_N, RESIN, KPDPWR+RESIN) sources.
+This peripheral is connected to the host processor via the SPMI interface.
+
+Required properties:
+- compatible:	Must be "qcom,qpnp-power-on"
+- reg:		Specifies the SPMI address and size for this PON (power-on)
+		peripheral.
+- interrupts:	Specifies the interrupt associated with PON.
+- interrupt-names:	Specify the interrupt names associated with interrupts.
+			Must be one of "kpdpwr", "kpdpwr-bark", "resin",
+			"resin-bark", "cblpwr", "kpdpwr-resin-bark".Bark
+			interrupts are associated with system reset
+			configuration to allow default reset configuration to
+			be activated. If system reset configuration is not
+			supported then bark interrupts are nops. Additionally,
+			the "pmic-wd-bark" interrupt can be added if the system
+			needs to handle PMIC watch dog barks.
+
+Optional properties:
+- qcom,pon-dbc-delay		The debounce delay for the power-key interrupt
+				specified in us.
+				Possible values for GEN1 PON are:
+				15625, 31250, 62500, 125000, 250000, 500000,
+				1000000 and 2000000.
+				Possible values for GEN2 PON are:
+				62, 123, 245, 489, 977, 1954, 3907, 7813,
+				15625, 31250, 62500, 125000 and 250000.
+				Intermediate value is rounded down to the
+				nearest valid value.
+- qcom,pon_1 ...pon_n		These represent the child nodes which describe
+				the properties (reset, key) for each of the pon
+				reset source. All the child nodes are optional.
+				If none of them is specified, the driver fails
+				to register.
+- qcom,system-reset		Specifies that this PON peripheral can be used
+				to reset the system. This property can only be
+				used by one device on the system. It is an
+				error to include it more than once.
+- qcom,s3-debounce		The debounce delay for stage3 reset trigger in
+				secs. The values range from 0 to 128.
+- qcom,s3-src			The source for stage 3 reset. It can be one of
+				"kpdpwr", "resin", "kpdpwr-or-resin" or
+				"kpdpwr-and-resin". The default value is
+				"kpdpwr-and-resin".
+- qcom,uvlo-panic		If this property is present, the device will
+				set to panic during reboot if this reboot is
+				due to under voltage lock out.
+- qcom,clear-warm-reset		Specifies that the WARM_RESET reason registers
+				need to be cleared for this target. The
+				property is used for the targets which have a
+				hardware feature to catch resets which aren't
+				triggered by the MSM.
+				In such cases clearing WARM_REASON registers
+				across MSM resets keeps the registers in good
+				state.
+- qcom,secondary-pon-reset	Boolean property which indicates that the PON
+				peripheral is a secondary PON device which
+				needs to be configured during reset in addition
+				to the primary PON device that is configured
+				for system reset through qcom,system-reset
+				property.
+				This should not be defined along with the
+				qcom,system-reset property.
+- qcom,store-hard-reset-reason	Boolean property which if set will store the
+				hardware reset reason to SOFT_RB_SPARE register
+				of the core PMIC PON peripheral.
+- qcom,warm-reset-poweroff-type	Poweroff type required to be configured on
+				PS_HOLD reset control register when the system
+				goes for warm reset. If this property is not
+				specified, then the default type, warm reset
+				will be configured to PS_HOLD reset control
+				register.
+- qcom,hard-reset-poweroff-type	Same description as qcom,warm-reset-poweroff-
+				type but this applies for the system hard reset
+				case.
+- qcom,shutdown-poweroff-type	Same description as qcom,warm-reset-poweroff-
+				type but this applies for the system shutdown
+				case.
+
+
+All the below properties are in the sub-node section (properties of the child
+node).
+
+Sub-nodes (if defined) should belong to either a PON configuration or a
+regulator configuration.
+
+Regulator sub-node required properties:
+- regulator-name		Regulator name for the PON regulator that
+				is being configured.
+- qcom,pon-spare-reg-addr	Register offset from the base address of the
+				PON peripheral that needs to be configured for
+				the regulator being controlled.
+- qcom,pon-spare-reg-bit	Bit position in the specified register that
+				needs to be configured for the regulator being
+				controlled.
+
+PON sub-node required properties:
+- qcom,pon-type			The type of PON/RESET source. The driver
+				currently supports KPDPWR(0), RESIN(1) and
+				CBLPWR(2) pon/reset sources.
+
+PON sub-node optional properties:
+- qcom,pull-up			The initial state of the reset pin under
+				consideration.
+				0 = No pull-up
+				1 = pull-up enabled
+				This property is set to '0' if not specified.
+- qcom,support-reset		Indicates if this PON source supports
+				reset functionality.
+				0 = Not supported
+				1 = Supported
+				If this property is not defined, then do not
+				modify S2 reset values.
+- qcom,use-bark                 Specify if this pon type needs to handle bark
+				irq.
+- linux,code                    The input key-code associated with the reset
+				source.
+                                The reset source in its default configuration
+				can be used to support standard keys.
+
+The below mentioned properties are required only when qcom,support-reset DT
+property is defined and is set to 1.
+
+- qcom,s1-timer			The debounce timer for the BARK interrupt for
+				that reset source. Value is specified in ms.
+				Supported values are:
+				- 0, 32, 56, 80, 128, 184, 272, 408, 608, 904
+				  1352, 2048, 3072, 4480, 6720, 10256
+- qcom,s2-timer			The debounce timer for the S2 reset specified
+				in ms. On the expiry of this timer, the PMIC
+				executes the reset sequence.
+				Supported values are:
+				- 0, 10, 50, 100, 250, 500, 1000, 2000
+- qcom,s2-type			The type of reset associated with this source.
+				The supported resets are:
+				SOFT(0), WARM(1), SHUTDOWN(4), HARD(7)
+
+Example:
+	qcom,power-on@800 {
+		compatible = "qcom,qpnp-power-on";
+		reg = <0x800 0x100>;
+		interrupts = <0x0 0x8 0x0>,
+			     <0x0 0x8 0x1>,
+			     <0x0 0x8 0x4>,
+			     <0x0 0x8 0x5>;
+		interrupt-names = "kpdpwr", "resin",
+				"resin-bark", "kpdpwr-resin-bark";
+		qcom,pon-dbc-delay = <15625>;
+		qcom,system-reset;
+		qcom,s3-debounce = <32>;
+		qcom,s3-src = "resin";
+		qcom,clear-warm-reset;
+		qcom,store-hard-reset-reason;
+
+		qcom,pon_1 {
+			qcom,pon-type = <0>;
+			qcom,pull-up = <1>;
+			linux,code = <116>;
+		};
+
+		qcom,pon_2 {
+			qcom,pon-type = <1>;
+			qcom,support-reset = <1>;
+			qcom,pull-up = <1>;
+			qcom,s1-timer = <0>;
+			qcom,s2-timer = <2000>;
+			qcom,s2-type = <1>;
+			linux,code = <114>;
+			qcom,use-bark;
+		};
+
+		qcom,pon_3 {
+			qcom,pon-type = <3>;
+			qcom,support-reset = <1>;
+			qcom,s1-timer = <6720>;
+			qcom,s2-timer = <2000>;
+			qcom,s2-type = <7>;
+			qcom,pull-up = <1>;
+			qcom,use-bark;
+		};
+	};
+	qcom,power-on@800 {
+		compatible = "qcom,qpnp-power-on";
+		reg = <0x800 0x100>;
+		qcom,secondary-pon-reset;
+		qcom,hard-reset-poweroff-type = <PON_POWER_OFF_SHUTDOWN>;
+
+		pon_perph_reg: qcom,pon_perph_reg {
+			regulator-name = "pon_spare_reg";
+			qcom,pon-spare-reg-addr = <0x8c>;
+			qcom,pon-spare-reg-bit = <1>;
+		};
+	};
diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt
index 52daff6..f74123c 100644
--- a/Documentation/sysctl/kernel.txt
+++ b/Documentation/sysctl/kernel.txt
@@ -23,8 +23,10 @@
 - auto_msgmni
 - bootloader_type	     [ X86 only ]
 - bootloader_version	     [ X86 only ]
+- boot_reason		     [ ARM and ARM64 only ]
 - callhome		     [ S390 only ]
 - cap_last_cap
+- cold_boot		     [ ARM and ARM64 only ]
 - core_pattern
 - core_pipe_limit
 - core_uses_pid
@@ -164,6 +166,19 @@
 
 ==============================================================
 
+boot_reason:
+
+ARM and ARM64 -- reason for device boot
+
+A single bit will be set in the unsigned integer value to identify the
+reason the device was booted / powered on. The value will be zero if this
+feature is not supported on the ARM device being booted.
+
+See the power-on-status field definitions in
+Documentation/arm/msm/boot.txt for Qualcomm's family of devices.
+
+==============================================================
+
 callhome:
 
 Controls the kernel's callhome behavior in case of a kernel panic.
@@ -184,6 +199,16 @@
 Highest valid capability of the running kernel.  Exports
 CAP_LAST_CAP from the kernel.
 
+===============================================================
+
+cold_boot
+
+ARM and ARM64 -- indicator for system cold boot
+
+A single bit will be set in the unsigned integer value to identify
+whether the device was booted from a cold or warm state. Zero
+indicating a warm boot and one indicating a cold boot.
+
 ==============================================================
 
 core_pattern:
diff --git a/arch/arm/include/asm/processor.h b/arch/arm/include/asm/processor.h
index 8a1e8e9..b2c5d79 100644
--- a/arch/arm/include/asm/processor.h
+++ b/arch/arm/include/asm/processor.h
@@ -30,6 +30,9 @@
 #define STACK_TOP_MAX	TASK_SIZE
 #endif
 
+extern unsigned int boot_reason;
+extern unsigned int cold_boot;
+
 struct debug_info {
 #ifdef CONFIG_HAVE_HW_BREAKPOINT
 	struct perf_event	*hbp[ARM_MAX_HBP_SLOTS];
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index ccf79ca..01efdf478 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -116,6 +116,12 @@ EXPORT_SYMBOL(elf_hwcap2);
 char* (*arch_read_hardware_id)(void);
 EXPORT_SYMBOL(arch_read_hardware_id);
 
+unsigned int boot_reason;
+EXPORT_SYMBOL(boot_reason);
+
+unsigned int cold_boot;
+EXPORT_SYMBOL(cold_boot);
+
 #ifdef MULTI_CPU
 struct processor processor __ro_after_init;
 #endif
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 3aff389..820c23d2 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -606,6 +606,18 @@
 	  Say Y here to experiment with turning CPUs off and on.  CPUs
 	  can be controlled through /sys/devices/system/cpu.
 
+# The GPIO number here must be sorted by descending number. In case of
+# a multiplatform kernel, we just want the highest value required by the
+# selected platforms.
+config ARCH_NR_GPIO
+	int
+	default 1024 if ARCH_QCOM
+	default 256
+	help
+	  Maximum number of GPIOs in the system.
+
+	  If unsure, leave the default value.
+
 # Common NUMA Features
 config NUMA
 	bool "Numa Memory Allocation and Scheduler Support"
diff --git a/arch/arm64/boot/dts/qcom/msmskunk.dtsi b/arch/arm64/boot/dts/qcom/msmskunk.dtsi
index c15a508..f53280a 100644
--- a/arch/arm64/boot/dts/qcom/msmskunk.dtsi
+++ b/arch/arm64/boot/dts/qcom/msmskunk.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -16,6 +16,7 @@
 #include <dt-bindings/clock/qcom,dispcc-skunk.h>
 #include <dt-bindings/clock/qcom,gpucc-skunk.h>
 #include <dt-bindings/clock/qcom,videocc-skunk.h>
+#include <dt-bindings/clock/qcom,cpucc-skunk.h>
 #include <dt-bindings/regulator/qcom,rpmh-regulator.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 #include <dt-bindings/soc/qcom,tcs-mbox.h>
@@ -497,6 +498,13 @@
 		#reset-cells = <1>;
 	};
 
+	clock_cpucc: qcom,cpucc {
+		compatible = "qcom,dummycc";
+		clock-output-names = "cpucc_clocks";
+		#clock-cells = <1>;
+		#reset-cells = <1>;
+	};
+
 	ufsphy_mem: ufsphy_mem@1d87000 {
 		reg = <0x1d87000 0xda8>; /* PHY regs */
 		reg-names = "phy_mem";
@@ -690,9 +698,9 @@
 
 		interrupts = <0 266 1>;
 		vdd_cx-supply = <&pm8998_s9_level>;
-		vdd_cx-voltage = <RPMH_REGULATOR_LEVEL_MAX>;
+		vdd_cx-voltage = <RPMH_REGULATOR_LEVEL_TURBO>;
 		vdd_mx-supply = <&pm8998_s6_level>;
-		vdd_mx-uV = <RPMH_REGULATOR_LEVEL_MAX>;
+		vdd_mx-uV = <RPMH_REGULATOR_LEVEL_TURBO>;
 		qcom,firmware-name = "modem";
 		qcom,pil-self-auth;
 		qcom,sysmon-id = <0>;
@@ -721,7 +729,7 @@
 
 		vdd_cx-supply = <&pm8998_s9_level>;
 		qcom,proxy-reg-names = "vdd_cx";
-		qcom,vdd_cx-uV-uA = <RPMH_REGULATOR_LEVEL_MAX 100000>;
+		qcom,vdd_cx-uV-uA = <RPMH_REGULATOR_LEVEL_TURBO 100000>;
 
 		clocks = <&clock_gcc RPMH_CXO_CLK>;
 		clock-names = "xo";
@@ -753,7 +761,7 @@
 
 		vdd_cx-supply = <&pm8998_l27_level>;
 		vdd_px-supply = <&pm8998_lvs2>;
-		qcom,vdd_cx-uV-uA = <RPMH_REGULATOR_LEVEL_MAX 0>;
+		qcom,vdd_cx-uV-uA = <RPMH_REGULATOR_LEVEL_TURBO 0>;
 		qcom,proxy-reg-names = "vdd_cx", "vdd_px";
 		qcom,keep-proxy-regs-on;
 
@@ -802,9 +810,9 @@
 
 		vdd_cx-supply = <&pm8998_s9_level>;
 		qcom,proxy-reg-names = "vdd_cx";
-		qcom,vdd_cx-uV-uA = <RPMH_REGULATOR_LEVEL_MAX 100000>;
+		qcom,vdd_cx-uV-uA = <RPMH_REGULATOR_LEVEL_TURBO 100000>;
 		vdd_mx-supply = <&pm8998_s6_level>;
-		vdd_mx-uV = <RPMH_REGULATOR_LEVEL_MAX 100000>;
+		vdd_mx-uV = <RPMH_REGULATOR_LEVEL_TURBO 100000>;
 
 		clocks = <&clock_gcc RPMH_CXO_CLK>;
 		clock-names = "xo";
@@ -837,7 +845,7 @@
 
 		vdd_cx-supply = <&pm8998_s9_level>;
 		qcom,proxy-reg-names = "vdd_cx";
-		qcom,vdd_cx-uV-uA = <RPMH_REGULATOR_LEVEL_MAX 100000>;
+		qcom,vdd_cx-uV-uA = <RPMH_REGULATOR_LEVEL_TURBO 100000>;
 
 		clocks = <&clock_gcc RPMH_CXO_CLK>;
 		clock-names = "xo";
diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
index 60e3482..a4af9f0 100644
--- a/arch/arm64/include/asm/processor.h
+++ b/arch/arm64/include/asm/processor.h
@@ -49,6 +49,9 @@
 extern phys_addr_t arm64_dma_phys_limit;
 #define ARCH_LOW_ADDRESS_LIMIT	(arm64_dma_phys_limit - 1)
 
+extern unsigned int boot_reason;
+extern unsigned int cold_boot;
+
 struct debug_info {
 	/* Have we suspended stepping by a debugger? */
 	int			suspended_step;
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
index 75f0efe..9f79515 100644
--- a/arch/arm64/kernel/setup.c
+++ b/arch/arm64/kernel/setup.c
@@ -67,6 +67,12 @@
 
 phys_addr_t __fdt_pointer __initdata;
 
+unsigned int boot_reason;
+EXPORT_SYMBOL(boot_reason);
+
+unsigned int cold_boot;
+EXPORT_SYMBOL(cold_boot);
+
 const char *machine_name;
 /*
  * Standard memory resources
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 22d1760..958e255 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -294,7 +294,8 @@ static const char * const fw_path[] = {
 	"/lib/firmware/updates/" UTS_RELEASE,
 	"/lib/firmware/updates",
 	"/lib/firmware/" UTS_RELEASE,
-	"/lib/firmware"
+	"/lib/firmware",
+	"/firmware/image"
 };
 
 /*
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
index c05dabd..59e9899 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
@@ -128,7 +128,7 @@ static int dsi_display_debugfs_init(struct dsi_display *display)
 
 static int dsi_dipslay_debugfs_deinit(struct dsi_display *display)
 {
-	debugfs_remove(display->root);
+	debugfs_remove_recursive(display->root);
 
 	return 0;
 }
@@ -999,6 +999,7 @@ static int dsi_display_clocks_init(struct dsi_display *display)
 	src->byte_clk = devm_clk_get(&display->pdev->dev, "src_byte_clk");
 	if (IS_ERR_OR_NULL(src->byte_clk)) {
 		rc = PTR_ERR(src->byte_clk);
+		src->byte_clk = NULL;
 		pr_err("failed to get src_byte_clk, rc=%d\n", rc);
 		goto error;
 	}
@@ -1006,6 +1007,7 @@ static int dsi_display_clocks_init(struct dsi_display *display)
 	src->pixel_clk = devm_clk_get(&display->pdev->dev, "src_pixel_clk");
 	if (IS_ERR_OR_NULL(src->pixel_clk)) {
 		rc = PTR_ERR(src->pixel_clk);
+		src->pixel_clk = NULL;
 		pr_err("failed to get src_pixel_clk, rc=%d\n", rc);
 		goto error;
 	}
@@ -1014,6 +1016,7 @@ static int dsi_display_clocks_init(struct dsi_display *display)
 	if (IS_ERR_OR_NULL(mux->byte_clk)) {
 		rc = PTR_ERR(mux->byte_clk);
 		pr_err("failed to get mux_byte_clk, rc=%d\n", rc);
+		mux->byte_clk = NULL;
 		/*
 		 * Skip getting rest of clocks since one failed. This is a
 		 * non-critical failure since these clocks are requied only for
@@ -1026,6 +1029,7 @@ static int dsi_display_clocks_init(struct dsi_display *display)
 	mux->pixel_clk = devm_clk_get(&display->pdev->dev, "mux_pixel_clk");
 	if (IS_ERR_OR_NULL(mux->pixel_clk)) {
 		rc = PTR_ERR(mux->pixel_clk);
+		mux->pixel_clk = NULL;
 		pr_err("failed to get mux_pixel_clk, rc=%d\n", rc);
 		/*
 		 * Skip getting rest of clocks since one failed. This is a
@@ -1039,6 +1043,7 @@ static int dsi_display_clocks_init(struct dsi_display *display)
 	shadow->byte_clk = devm_clk_get(&display->pdev->dev, "shadow_byte_clk");
 	if (IS_ERR_OR_NULL(shadow->byte_clk)) {
 		rc = PTR_ERR(shadow->byte_clk);
+		shadow->byte_clk = NULL;
 		pr_err("failed to get shadow_byte_clk, rc=%d\n", rc);
 		/*
 		 * Skip getting rest of clocks since one failed. This is a
@@ -1053,6 +1058,7 @@ static int dsi_display_clocks_init(struct dsi_display *display)
 					 "shadow_pixel_clk");
 	if (IS_ERR_OR_NULL(shadow->pixel_clk)) {
 		rc = PTR_ERR(shadow->pixel_clk);
+		shadow->pixel_clk = NULL;
 		pr_err("failed to get shadow_pixel_clk, rc=%d\n", rc);
 		/*
 		 * Skip getting rest of clocks since one failed. This is a
@@ -1888,6 +1894,8 @@ int dsi_display_unbind(struct dsi_display *display)
 			       display->name, i, rc);
 	}
 
+	(void)dsi_dipslay_debugfs_deinit(display);
+
 	mutex_unlock(&display->display_lock);
 	return rc;
 }
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h
index 403160c..cca934d 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -239,6 +239,20 @@ struct sde_connector_state {
 	((S) ? to_sde_connector_state((S))->out_fb : 0)
 
 /**
+ * sde_connector_get_topology_name - helper accessor to retrieve topology_name
+ * @connector: pointer to drm connector
+ * Returns: value of the CONNECTOR_PROP_TOPOLOGY_NAME property or 0
+ */
+static inline uint64_t sde_connector_get_topology_name(
+		struct drm_connector *connector)
+{
+	if (!connector || !connector->state)
+		return 0;
+	return sde_connector_get_property(connector->state,
+			CONNECTOR_PROP_TOPOLOGY_NAME);
+}
+
+/**
  * sde_connector_init - create drm connector object for a given display
  * @dev: Pointer to drm device struct
  * @encoder: Pointer to associated encoder
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index 535b7d6..fd57404 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -350,6 +350,9 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc,
 	if (!conn) {
 		SDE_ERROR_ENC(sde_enc, "failed to find attached connector\n");
 		return;
+	} else if (!conn->state) {
+		SDE_ERROR_ENC(sde_enc, "invalid connector state\n");
+		return;
 	}
 
 	/* Reserve dynamic resources now. Indicating non-AtomicTest phase */
@@ -364,8 +367,11 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc,
 	for (i = 0; i < sde_enc->num_phys_encs; i++) {
 		struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
 
-		if (phys && phys->ops.mode_set)
-			phys->ops.mode_set(phys, mode, adj_mode);
+		if (phys) {
+			phys->connector = conn->state->connector;
+			if (phys->ops.mode_set)
+				phys->ops.mode_set(phys, mode, adj_mode);
+		}
 	}
 }
 
@@ -452,11 +458,10 @@ static void sde_encoder_virt_disable(struct drm_encoder *drm_enc)
 	for (i = 0; i < sde_enc->num_phys_encs; i++) {
 		struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
 
-		if (phys && phys->ops.disable && !phys->ops.is_master(phys)) {
-			phys->ops.disable(phys);
-
-			atomic_set(&phys->vsync_cnt, 0);
-			atomic_set(&phys->underrun_cnt, 0);
+		if (phys) {
+			if (phys->ops.disable && !phys->ops.is_master(phys))
+				phys->ops.disable(phys);
+			phys->connector = NULL;
 		}
 	}
 
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
index 28548f3..22d187e 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
@@ -147,6 +147,7 @@ enum sde_intr_idx {
  *	tied to a specific panel / sub-panel. Abstract type, sub-classed by
  *	phys_vid or phys_cmd for video mode or command mode encs respectively.
  * @parent:		Pointer to the containing virtual encoder
+ * @connector:		If a mode is set, cached pointer to the active connector
  * @ops:		Operations exposed to the virtual encoder
  * @parent_ops:		Callbacks exposed by the parent to the phys_enc
  * @hw_mdptop:		Hardware interface to the top registers
@@ -160,7 +161,6 @@ enum sde_intr_idx {
  * @intf_mode:		Interface mode
  * @intf_idx:		Interface index on sde hardware
  * @spin_lock:		Lock for IRQ purposes
- * @mode_3d:		3D mux configuration
  * @enable_state:	Enable state tracking
  * @vblank_refcount:	Reference count of vblank request
  * @vsync_cnt:		Vsync count for the physical encoder
@@ -168,6 +168,7 @@ enum sde_intr_idx {
  */
 struct sde_encoder_phys {
 	struct drm_encoder *parent;
+	struct drm_connector *connector;
 	struct sde_encoder_phys_ops ops;
 	struct sde_encoder_virt_ops parent_ops;
 	struct sde_hw_mdp *hw_mdptop;
@@ -180,7 +181,6 @@ struct sde_encoder_phys {
 	enum sde_intf_mode intf_mode;
 	enum sde_intf intf_idx;
 	spinlock_t spin_lock;
-	enum sde_3d_blend_mode mode_3d;
 	enum sde_enc_enable_state enable_state;
 	atomic_t vblank_refcount;
 	atomic_t vsync_cnt;
@@ -338,4 +338,18 @@ void sde_encoder_phys_setup_cdm(struct sde_encoder_phys *phys_enc,
  */
 void sde_encoder_helper_trigger_start(struct sde_encoder_phys *phys_enc);
 
+
+static inline enum sde_3d_blend_mode sde_encoder_helper_get_3d_blend_mode(
+		struct sde_encoder_phys *phys_enc)
+{
+	enum sde_rm_topology_name topology;
+
+	topology = sde_connector_get_topology_name(phys_enc->connector);
+	if (phys_enc->split_role == ENC_ROLE_SOLO &&
+			topology == SDE_RM_TOPOLOGY_DUALPIPEMERGE)
+		return BLEND_3D_H_ROW_INT;
+
+	return BLEND_3D_NONE;
+}
+
 #endif /* __sde_encoder_phys_H__ */
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
index 79da71b..3f478fa 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
@@ -341,9 +341,10 @@ static void sde_encoder_phys_cmd_pingpong_config(
 	drm_mode_debug_printmodeline(&phys_enc->cached_mode);
 
 	intf_cfg.intf = cmd_enc->intf_idx;
-	intf_cfg.mode_3d = phys_enc->mode_3d;
 	intf_cfg.intf_mode_sel = SDE_CTL_MODE_SEL_CMD;
 	intf_cfg.stream_sel = cmd_enc->stream_sel;
+	intf_cfg.mode_3d = sde_encoder_helper_get_3d_blend_mode(phys_enc);
+
 	phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl, &intf_cfg);
 
 	sde_encoder_phys_cmd_tearcheck_config(phys_enc);
@@ -649,7 +650,6 @@ struct sde_encoder_phys *sde_encoder_phys_cmd_init(
 	phys_enc->split_role = p->split_role;
 	phys_enc->intf_mode = INTF_MODE_CMD;
 	spin_lock_init(&phys_enc->spin_lock);
-	phys_enc->mode_3d = BLEND_3D_NONE;
 	cmd_enc->stream_sel = 0;
 	phys_enc->enable_state = SDE_ENC_DISABLED;
 	atomic_set(&cmd_enc->pending_cnt, 0);
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
index 8c5e604..b94f64f 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
@@ -248,22 +248,26 @@ static bool sde_encoder_phys_vid_mode_fixup(
 static void sde_encoder_phys_vid_setup_timing_engine(
 		struct sde_encoder_phys *phys_enc)
 {
-	struct sde_encoder_phys_vid *vid_enc =
-		to_sde_encoder_phys_vid(phys_enc);
-	struct drm_display_mode mode = phys_enc->cached_mode;
+	struct sde_encoder_phys_vid *vid_enc;
+	struct drm_display_mode mode;
 	struct intf_timing_params timing_params = { 0 };
 	const struct sde_format *fmt = NULL;
 	u32 fmt_fourcc = DRM_FORMAT_RGB888;
 	unsigned long lock_flags;
 	struct sde_hw_intf_cfg intf_cfg = { 0 };
 
-	if (!phys_enc ||
-			!vid_enc->hw_intf->ops.setup_timing_gen ||
-			!phys_enc->hw_ctl->ops.setup_intf_cfg) {
+	if (!phys_enc || !phys_enc->hw_ctl->ops.setup_intf_cfg) {
 		SDE_ERROR("invalid encoder %d\n", phys_enc != 0);
 		return;
 	}
 
+	mode = phys_enc->cached_mode;
+	vid_enc = to_sde_encoder_phys_vid(phys_enc);
+	if (!vid_enc->hw_intf->ops.setup_timing_gen) {
+		SDE_ERROR("timing engine setup is not supported\n");
+		return;
+	}
+
 	SDE_DEBUG_VIDENC(vid_enc, "enabling mode:\n");
 	drm_mode_debug_printmodeline(&mode);
 
@@ -286,10 +290,9 @@ static void sde_encoder_phys_vid_setup_timing_engine(
 	SDE_DEBUG_VIDENC(vid_enc, "fmt_fourcc 0x%X\n", fmt_fourcc);
 
 	intf_cfg.intf = vid_enc->hw_intf->idx;
-	intf_cfg.wb = SDE_NONE;
-	intf_cfg.mode_3d = phys_enc->mode_3d;
 	intf_cfg.intf_mode_sel = SDE_CTL_MODE_SEL_VID;
 	intf_cfg.stream_sel = 0; /* Don't care value for video mode */
+	intf_cfg.mode_3d = sde_encoder_helper_get_3d_blend_mode(phys_enc);
 
 	spin_lock_irqsave(&phys_enc->spin_lock, lock_flags);
 	vid_enc->hw_intf->ops.setup_timing_gen(vid_enc->hw_intf,
@@ -367,8 +370,7 @@ static int sde_encoder_phys_vid_register_irq(struct sde_encoder_phys *phys_enc,
 	enum sde_intr_type intr_type, int *irq_idx,
 	void (*irq_func)(void *, int), const char *irq_name)
 {
-	struct sde_encoder_phys_vid *vid_enc =
-			to_sde_encoder_phys_vid(phys_enc);
+	struct sde_encoder_phys_vid *vid_enc;
 	struct sde_irq_callback irq_cb;
 	int ret = 0;
 
@@ -377,6 +379,7 @@ static int sde_encoder_phys_vid_register_irq(struct sde_encoder_phys *phys_enc,
 		return -EINVAL;
 	}
 
+	vid_enc = to_sde_encoder_phys_vid(phys_enc);
 	*irq_idx = sde_core_irq_idx_lookup(phys_enc->sde_kms, intr_type,
 			vid_enc->hw_intf->idx);
 	if (*irq_idx < 0) {
@@ -418,14 +421,14 @@ static int sde_encoder_phys_vid_register_irq(struct sde_encoder_phys *phys_enc,
 static int sde_encoder_phys_vid_unregister_irq(
 	struct sde_encoder_phys *phys_enc, int irq_idx)
 {
-	struct sde_encoder_phys_vid *vid_enc =
-			to_sde_encoder_phys_vid(phys_enc);
+	struct sde_encoder_phys_vid *vid_enc;
 
 	if (!phys_enc) {
 		SDE_ERROR("invalid encoder\n");
 		goto end;
 	}
 
+	vid_enc = to_sde_encoder_phys_vid(phys_enc);
 	sde_core_irq_disable(phys_enc->sde_kms, &irq_idx, 1);
 
 	sde_core_irq_register_callback(phys_enc->sde_kms, irq_idx, NULL);
@@ -441,17 +444,17 @@ static void sde_encoder_phys_vid_mode_set(
 		struct drm_display_mode *mode,
 		struct drm_display_mode *adj_mode)
 {
-	struct sde_encoder_phys_vid *vid_enc =
-		to_sde_encoder_phys_vid(phys_enc);
 	struct sde_rm *rm = &phys_enc->sde_kms->rm;
 	struct sde_rm_hw_iter iter;
 	int i, instance;
+	struct sde_encoder_phys_vid *vid_enc;
 
 	if (!phys_enc) {
 		SDE_ERROR("invalid encoder\n");
 		return;
 	}
 
+	vid_enc = to_sde_encoder_phys_vid(phys_enc);
 	phys_enc->cached_mode = *adj_mode;
 	SDE_DEBUG_VIDENC(vid_enc, "caching mode:\n");
 	drm_mode_debug_printmodeline(adj_mode);
@@ -478,15 +481,16 @@ static int sde_encoder_phys_vid_control_vblank_irq(
 		struct sde_encoder_phys *phys_enc,
 		bool enable)
 {
-	struct sde_encoder_phys_vid *vid_enc =
-		to_sde_encoder_phys_vid(phys_enc);
 	int ret = 0;
+	struct sde_encoder_phys_vid *vid_enc;
 
 	if (!phys_enc) {
 		SDE_ERROR("invalid encoder\n");
 		return -EINVAL;
 	}
 
+	vid_enc = to_sde_encoder_phys_vid(phys_enc);
+
 	/* Slave encoders don't report vblank */
 	if (!sde_encoder_phys_vid_is_master(phys_enc))
 		return 0;
@@ -517,17 +521,21 @@ static int sde_encoder_phys_vid_control_vblank_irq(
 
 static void sde_encoder_phys_vid_enable(struct sde_encoder_phys *phys_enc)
 {
-	struct sde_encoder_phys_vid *vid_enc =
-		to_sde_encoder_phys_vid(phys_enc);
-	struct sde_hw_intf *intf = vid_enc->hw_intf;
-	struct sde_hw_ctl *ctl = phys_enc->hw_ctl;
+	struct sde_encoder_phys_vid *vid_enc;
+	struct sde_hw_intf *intf;
+	struct sde_hw_ctl *ctl;
 	u32 flush_mask = 0;
 	int ret;
 
 	if (!phys_enc) {
 		SDE_ERROR("invalid encoder\n");
 		return;
-	} else if (!vid_enc->hw_intf || !phys_enc->hw_ctl) {
+	}
+
+	vid_enc = to_sde_encoder_phys_vid(phys_enc);
+	intf = vid_enc->hw_intf;
+	ctl = phys_enc->hw_ctl;
+	if (!vid_enc->hw_intf || !phys_enc->hw_ctl) {
 		SDE_ERROR("invalid hw_intf %d hw_ctl %d\n",
 				vid_enc->hw_intf != 0, phys_enc->hw_ctl != 0);
 		return;
@@ -574,13 +582,15 @@ static void sde_encoder_phys_vid_enable(struct sde_encoder_phys *phys_enc)
 static void sde_encoder_phys_vid_disable(struct sde_encoder_phys *phys_enc)
 {
 	unsigned long lock_flags;
-	struct sde_encoder_phys_vid *vid_enc =
-			to_sde_encoder_phys_vid(phys_enc);
+	struct sde_encoder_phys_vid *vid_enc;
 
 	if (!phys_enc) {
 		SDE_ERROR("invalid encoder\n");
 		return;
-	} else if (!vid_enc->hw_intf || !phys_enc->hw_ctl) {
+	}
+
+	vid_enc = to_sde_encoder_phys_vid(phys_enc);
+	if (!vid_enc->hw_intf || !phys_enc->hw_ctl) {
 		SDE_ERROR("invalid hw_intf %d hw_ctl %d\n",
 				vid_enc->hw_intf != 0, phys_enc->hw_ctl != 0);
 		return;
@@ -624,13 +634,14 @@ static void sde_encoder_phys_vid_disable(struct sde_encoder_phys *phys_enc)
 
 static void sde_encoder_phys_vid_destroy(struct sde_encoder_phys *phys_enc)
 {
-	struct sde_encoder_phys_vid *vid_enc =
-	    to_sde_encoder_phys_vid(phys_enc);
+	struct sde_encoder_phys_vid *vid_enc;
 
 	if (!phys_enc) {
 		SDE_ERROR("invalid encoder\n");
 		return;
 	}
+
+	vid_enc = to_sde_encoder_phys_vid(phys_enc);
 	SDE_DEBUG_VIDENC(vid_enc, "\n");
 	kfree(vid_enc);
 }
@@ -640,14 +651,20 @@ static void sde_encoder_phys_vid_get_hw_resources(
 		struct sde_encoder_hw_resources *hw_res,
 		struct drm_connector_state *conn_state)
 {
-	struct sde_encoder_phys_vid *vid_enc =
-		to_sde_encoder_phys_vid(phys_enc);
+	struct sde_encoder_phys_vid *vid_enc;
 
-	if (!phys_enc || !hw_res || !vid_enc->hw_intf) {
+	if (!phys_enc || !hw_res) {
 		SDE_ERROR("invalid arg(s), enc %d hw_res %d conn_state %d\n",
 				phys_enc != 0, hw_res != 0, conn_state != 0);
 		return;
 	}
+
+	vid_enc = to_sde_encoder_phys_vid(phys_enc);
+	if (!vid_enc->hw_intf) {
+		SDE_ERROR("invalid arg(s), hw_intf\n");
+		return;
+	}
+
 	SDE_DEBUG_VIDENC(vid_enc, "\n");
 	hw_res->intfs[vid_enc->hw_intf->idx - INTF_0] = INTF_MODE_VIDEO;
 }
@@ -701,13 +718,14 @@ static void sde_encoder_phys_vid_handle_post_kickoff(
 		struct sde_encoder_phys *phys_enc)
 {
 	unsigned long lock_flags;
-	struct sde_encoder_phys_vid *vid_enc =
-			to_sde_encoder_phys_vid(phys_enc);
+	struct sde_encoder_phys_vid *vid_enc;
 
 	if (!phys_enc) {
 		SDE_ERROR("invalid encoder\n");
 		return;
 	}
+
+	vid_enc = to_sde_encoder_phys_vid(phys_enc);
 	SDE_DEBUG_VIDENC(vid_enc, "enable_state %d\n", phys_enc->enable_state);
 
 	/*
@@ -802,11 +820,6 @@ struct sde_encoder_phys *sde_encoder_phys_vid_init(
 	spin_lock_init(&phys_enc->spin_lock);
 	init_completion(&vid_enc->vblank_completion);
 	atomic_set(&phys_enc->vblank_refcount, 0);
-
-	DRM_INFO_ONCE("intf %d: 3d blend modes not yet supported\n",
-			vid_enc->hw_intf->idx);
-	phys_enc->mode_3d = BLEND_3D_NONE;
-
 	phys_enc->enable_state = SDE_ENC_DISABLED;
 
 	SDE_DEBUG_VIDENC(vid_enc, "created intf idx:%d\n", p->intf_idx);
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
index 32a25e4..665b044 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
@@ -278,6 +278,7 @@ static void sde_encoder_phys_wb_setup_cdp(struct sde_encoder_phys *phys_enc)
 
 	intf_cfg->intf = SDE_NONE;
 	intf_cfg->wb = hw_wb->idx;
+	intf_cfg->mode_3d = sde_encoder_helper_get_3d_blend_mode(phys_enc);
 
 	if (phys_enc->hw_ctl && phys_enc->hw_ctl->ops.setup_intf_cfg)
 		phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl,
diff --git a/drivers/gpu/drm/msm/sde/sde_formats.c b/drivers/gpu/drm/msm/sde/sde_formats.c
index 34e710d..5895158 100644
--- a/drivers/gpu/drm/msm/sde/sde_formats.c
+++ b/drivers/gpu/drm/msm/sde/sde_formats.c
@@ -102,163 +102,163 @@ flg, fm, np)                                                      \
 static const struct sde_format sde_format_map[] = {
 	INTERLEAVED_RGB_FMT(ARGB8888,
 		COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
+		C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
 		true, 4, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(ABGR8888,
 		COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
+		C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
 		true, 4, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(RGBA8888,
 		COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
+		C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
 		true, 4, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(BGRA8888,
 		COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+		C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
 		true, 4, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(BGRX8888,
 		COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+		C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
 		false, 4, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(XRGB8888,
 		COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
+		C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
 		false, 4, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(RGBX8888,
 		COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
+		C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
 		false, 4, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(RGB888,
 		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C1_B_Cb, C0_G_Y, C2_R_Cr, 0, 3,
+		C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3,
 		false, 3, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(BGR888,
 		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3,
+		C1_B_Cb, C0_G_Y, C2_R_Cr, 0, 3,
 		false, 3, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(RGB565,
 		0, COLOR_5BIT, COLOR_6BIT, COLOR_5BIT,
-		C1_B_Cb, C0_G_Y, C2_R_Cr, 0, 3,
+		C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3,
 		false, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(BGR565,
 		0, COLOR_5BIT, COLOR_6BIT, COLOR_5BIT,
-		C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3,
+		C1_B_Cb, C0_G_Y, C2_R_Cr, 0, 3,
 		false, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(ARGB1555,
 		COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
-		C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
+		C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
 		true, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(ABGR1555,
 		COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
-		C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
+		C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
 		true, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(RGBA5551,
 		COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
-		C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
+		C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
 		true, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(BGRA5551,
 		COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
-		C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+		C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
 		true, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(XRGB1555,
 		COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
-		C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
+		C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
 		false, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(XBGR1555,
 		COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
-		C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
+		C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
 		false, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(RGBX5551,
 		COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
-		C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
+		C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
 		false, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(BGRX5551,
 		COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
-		C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+		C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
 		false, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(ARGB4444,
 		COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
-		C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
+		C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
 		true, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(ABGR4444,
 		COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
-		C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
+		C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
 		true, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(RGBA4444,
 		COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
-		C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
+		C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
 		true, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(BGRA4444,
 		COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
-		C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+		C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
 		true, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(XRGB4444,
 		COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
-		C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
+		C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
 		false, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(XBGR4444,
 		COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
-		C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
+		C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
 		false, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(RGBX4444,
 		COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
-		C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
+		C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
 		false, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(BGRX4444,
 		COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
-		C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+		C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
 		false, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
@@ -312,13 +312,13 @@ static const struct sde_format sde_format_map[] = {
 
 	PLANAR_YUV_FMT(YUV420,
 		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C2_R_Cr, C1_B_Cb, C0_G_Y,
+		C0_G_Y, C1_B_Cb, C2_R_Cr,
 		false, SDE_CHROMA_420, 1, SDE_FORMAT_FLAG_YUV,
 		SDE_FETCH_LINEAR, 3),
 
 	PLANAR_YUV_FMT(YVU420,
 		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C1_B_Cb, C2_R_Cr, C0_G_Y,
+		C0_G_Y, C2_R_Cr, C1_B_Cb,
 		false, SDE_CHROMA_420, 1, SDE_FORMAT_FLAG_YUV,
 		SDE_FETCH_LINEAR, 3),
 };
@@ -330,19 +330,19 @@ static const struct sde_format sde_format_map[] = {
  * the data will be passed by user-space.
  */
 static const struct sde_format sde_format_map_ubwc[] = {
-	INTERLEAVED_RGB_FMT(BGR565,
+	INTERLEAVED_RGB_FMT(RGB565,
 		0, COLOR_5BIT, COLOR_6BIT, COLOR_5BIT,
 		C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3,
 		false, 2, 0,
 		SDE_FETCH_UBWC, 2),
 
-	INTERLEAVED_RGB_FMT(ABGR8888,
+	INTERLEAVED_RGB_FMT(RGBA8888,
 		COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
 		C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
 		true, 4, 0,
 		SDE_FETCH_UBWC, 2),
 
-	INTERLEAVED_RGB_FMT(XBGR8888,
+	INTERLEAVED_RGB_FMT(RGBX8888,
 		COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
 		C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
 		false, 4, 0,
@@ -447,12 +447,12 @@ static int _sde_format_get_plane_sizes_ubwc(
 			ALIGN(DIV_ROUND_UP(height / 2, uv_tile_height), 16),
 			4096);
 
-	} else if (fmt->base.pixel_format == DRM_FORMAT_ABGR8888 ||
-		fmt->base.pixel_format == DRM_FORMAT_XBGR8888    ||
-		fmt->base.pixel_format == DRM_FORMAT_BGR565) {
+	} else if (fmt->base.pixel_format == DRM_FORMAT_RGBA8888 ||
+		fmt->base.pixel_format == DRM_FORMAT_RGBX8888    ||
+		fmt->base.pixel_format == DRM_FORMAT_RGB565) {
 		uint32_t stride_alignment, aligned_bitstream_width;
 
-		if (fmt->base.pixel_format == DRM_FORMAT_BGR565)
+		if (fmt->base.pixel_format == DRM_FORMAT_RGB565)
 			stride_alignment = 128;
 		else
 			stride_alignment = 64;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
index 4e8033a..2f1bac7 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
@@ -21,7 +21,6 @@
 #define   CTL_TOP                       0x014
 #define   CTL_FLUSH                     0x018
 #define   CTL_START                     0x01C
-#define   CTL_PACK_3D                   0x020
 #define   CTL_SW_RESET                  0x030
 #define   CTL_LAYER_EXTN_OFFSET         0x40
 
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_top.c b/drivers/gpu/drm/msm/sde/sde_hw_top.c
index 8950a60..10d3917 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_top.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_top.c
@@ -141,34 +141,31 @@ struct sde_hw_mdp *sde_hw_mdptop_init(enum sde_mdp idx,
 		void __iomem *addr,
 		const struct sde_mdss_cfg *m)
 {
-	static struct sde_hw_mdp *c;
+	struct sde_hw_mdp *mdp;
 	const struct sde_mdp_cfg *cfg;
 
-	/* mdp top is singleton */
-	if (c)
-		return c;
-
-	c = kzalloc(sizeof(*c), GFP_KERNEL);
-	if (!c)
+	mdp = kzalloc(sizeof(*mdp), GFP_KERNEL);
+	if (!mdp)
 		return ERR_PTR(-ENOMEM);
 
-	cfg = _top_offset(idx, m, addr, &c->hw);
+	cfg = _top_offset(idx, m, addr, &mdp->hw);
 	if (IS_ERR_OR_NULL(cfg)) {
-		kfree(c);
+		kfree(mdp);
 		return ERR_PTR(-EINVAL);
 	}
 
 	/*
 	 * Assign ops
 	 */
-	c->idx = idx;
-	c->cap = cfg;
-	_setup_mdp_ops(&c->ops, c->cap->features);
+	mdp->idx = idx;
+	mdp->cap = cfg;
+	_setup_mdp_ops(&mdp->ops, mdp->cap->features);
 
 	/*
 	 * Perform any default initialization for the intf
 	 */
-	return c;
+
+	return mdp;
 }
 
 void sde_hw_mdp_destroy(struct sde_hw_mdp *mdp)
diff --git a/drivers/gpu/drm/msm/sde_power_handle.c b/drivers/gpu/drm/msm/sde_power_handle.c
index f8ba442..e2e8e60 100644
--- a/drivers/gpu/drm/msm/sde_power_handle.c
+++ b/drivers/gpu/drm/msm/sde_power_handle.c
@@ -220,9 +220,9 @@ static int sde_power_parse_dt_clock(struct platform_device *pdev,
 {
 	u32 i = 0, rc = 0;
 	const char *clock_name;
-	u32 clock_rate;
-	u32 clock_max_rate;
-	int num_clk;
+	u32 clock_rate = 0;
+	u32 clock_max_rate = 0;
+	int num_clk = 0;
 
 	if (!pdev || !mp) {
 		pr_err("invalid input param pdev:%pK mp:%pK\n", pdev, mp);
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 94360fe..78e84c6 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -164,6 +164,15 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called pmic8xxx-pwrkey.
 
+config INPUT_QPNP_POWER_ON
+	tristate "QPNP PMIC Power-on support"
+	depends on SPMI
+	help
+	  This option enables device driver support for the power-on
+	  functionality of Qualcomm Technologies, Inc. PNP PMICs.  It supports
+	  reporting the change in status of the KPDPWR_N line (connected to the
+	  power-key) as well as reset features.
+
 config INPUT_SPARCSPKR
 	tristate "SPARC Speaker support"
 	depends on PCI && SPARC64
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 64bf231..d55e4a6 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -59,6 +59,7 @@
 obj-$(CONFIG_INPUT_PM8941_PWRKEY)	+= pm8941-pwrkey.o
 obj-$(CONFIG_INPUT_PM8XXX_VIBRATOR)	+= pm8xxx-vibrator.o
 obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY)	+= pmic8xxx-pwrkey.o
+obj-$(CONFIG_INPUT_QPNP_POWER_ON)	+= qpnp-power-on.o
 obj-$(CONFIG_INPUT_POWERMATE)		+= powermate.o
 obj-$(CONFIG_INPUT_PWM_BEEPER)		+= pwm-beeper.o
 obj-$(CONFIG_INPUT_RB532_BUTTON)	+= rb532_button.o
diff --git a/drivers/input/misc/qpnp-power-on.c b/drivers/input/misc/qpnp-power-on.c
new file mode 100644
index 0000000..e1c16aa
--- /dev/null
+++ b/drivers/input/misc/qpnp-power-on.c
@@ -0,0 +1,2359 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/debugfs.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/input.h>
+#include <linux/log2.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/input/qpnp-power-on.h>
+#include <linux/power_supply.h>
+
+#define PMIC_VER_8941           0x01
+#define PMIC_VERSION_REG        0x0105
+#define PMIC_VERSION_REV4_REG   0x0103
+
+#define PMIC8941_V1_REV4	0x01
+#define PMIC8941_V2_REV4	0x02
+#define PON_PRIMARY		0x01
+#define PON_SECONDARY		0x02
+#define PON_1REG		0x03
+#define PON_GEN2_PRIMARY	0x04
+#define PON_GEN2_SECONDARY	0x05
+
+#define PON_OFFSET(subtype, offset_gen1, offset_gen2) \
+	(((subtype == PON_PRIMARY) || \
+	(subtype == PON_SECONDARY) || \
+	(subtype == PON_1REG)) ? offset_gen1 : offset_gen2)
+
+/* Common PNP defines */
+#define QPNP_PON_REVISION2(pon)			((pon)->base + 0x01)
+#define QPNP_PON_PERPH_SUBTYPE(pon)		((pon)->base + 0x05)
+
+/* PON common register addresses */
+#define QPNP_PON_RT_STS(pon)			((pon)->base + 0x10)
+#define QPNP_PON_PULL_CTL(pon)			((pon)->base + 0x70)
+#define QPNP_PON_DBC_CTL(pon)			((pon)->base + 0x71)
+
+/* PON/RESET sources register addresses */
+#define QPNP_PON_REASON1(pon) \
+	((pon)->base + PON_OFFSET((pon)->subtype, 0x8, 0xC0))
+#define QPNP_PON_WARM_RESET_REASON1(pon) \
+	((pon)->base + PON_OFFSET((pon)->subtype, 0xA, 0xC2))
+#define QPNP_POFF_REASON1(pon) \
+	((pon)->base + PON_OFFSET((pon)->subtype, 0xC, 0xC5))
+#define QPNP_PON_WARM_RESET_REASON2(pon)	((pon)->base + 0xB)
+#define QPNP_PON_OFF_REASON(pon)		((pon)->base + 0xC7)
+#define QPNP_FAULT_REASON1(pon)			((pon)->base + 0xC8)
+#define QPNP_S3_RESET_REASON(pon)		((pon)->base + 0xCA)
+#define QPNP_PON_KPDPWR_S1_TIMER(pon)		((pon)->base + 0x40)
+#define QPNP_PON_KPDPWR_S2_TIMER(pon)		((pon)->base + 0x41)
+#define QPNP_PON_KPDPWR_S2_CNTL(pon)		((pon)->base + 0x42)
+#define QPNP_PON_KPDPWR_S2_CNTL2(pon)		((pon)->base + 0x43)
+#define QPNP_PON_RESIN_S1_TIMER(pon)		((pon)->base + 0x44)
+#define QPNP_PON_RESIN_S2_TIMER(pon)		((pon)->base + 0x45)
+#define QPNP_PON_RESIN_S2_CNTL(pon)		((pon)->base + 0x46)
+#define QPNP_PON_RESIN_S2_CNTL2(pon)		((pon)->base + 0x47)
+#define QPNP_PON_KPDPWR_RESIN_S1_TIMER(pon)	((pon)->base + 0x48)
+#define QPNP_PON_KPDPWR_RESIN_S2_TIMER(pon)	((pon)->base + 0x49)
+#define QPNP_PON_KPDPWR_RESIN_S2_CNTL(pon)	((pon)->base + 0x4A)
+#define QPNP_PON_KPDPWR_RESIN_S2_CNTL2(pon)	((pon)->base + 0x4B)
+#define QPNP_PON_PS_HOLD_RST_CTL(pon)		((pon)->base + 0x5A)
+#define QPNP_PON_PS_HOLD_RST_CTL2(pon)		((pon)->base + 0x5B)
+#define QPNP_PON_WD_RST_S2_CTL(pon)		((pon)->base + 0x56)
+#define QPNP_PON_WD_RST_S2_CTL2(pon)		((pon)->base + 0x57)
+#define QPNP_PON_S3_SRC(pon)			((pon)->base + 0x74)
+#define QPNP_PON_S3_DBC_CTL(pon)		((pon)->base + 0x75)
+#define QPNP_PON_SMPL_CTL(pon)			((pon)->base + 0x7F)
+#define QPNP_PON_TRIGGER_EN(pon)		((pon)->base + 0x80)
+#define QPNP_PON_XVDD_RB_SPARE(pon)		((pon)->base + 0x8E)
+#define QPNP_PON_SOFT_RB_SPARE(pon)		((pon)->base + 0x8F)
+#define QPNP_PON_SEC_ACCESS(pon)		((pon)->base + 0xD0)
+
+#define QPNP_PON_SEC_UNLOCK			0xA5
+
+#define QPNP_PON_WARM_RESET_TFT			BIT(4)
+
+#define QPNP_PON_RESIN_PULL_UP			BIT(0)
+#define QPNP_PON_KPDPWR_PULL_UP			BIT(1)
+#define QPNP_PON_CBLPWR_PULL_UP			BIT(2)
+#define QPNP_PON_FAULT_PULL_UP			BIT(4)
+#define QPNP_PON_S2_CNTL_EN			BIT(7)
+#define QPNP_PON_S2_RESET_ENABLE		BIT(7)
+#define QPNP_PON_DELAY_BIT_SHIFT		6
+#define QPNP_PON_GEN2_DELAY_BIT_SHIFT		14
+
+#define QPNP_PON_S1_TIMER_MASK			(0xF)
+#define QPNP_PON_S2_TIMER_MASK			(0x7)
+#define QPNP_PON_S2_CNTL_TYPE_MASK		(0xF)
+
+#define QPNP_PON_DBC_DELAY_MASK(pon) \
+		PON_OFFSET((pon)->subtype, 0x7, 0xF)
+
+#define QPNP_PON_KPDPWR_N_SET			BIT(0)
+#define QPNP_PON_RESIN_N_SET			BIT(1)
+#define QPNP_PON_CBLPWR_N_SET			BIT(2)
+#define QPNP_PON_RESIN_BARK_N_SET		BIT(4)
+#define QPNP_PON_KPDPWR_RESIN_BARK_N_SET	BIT(5)
+
+#define QPNP_PON_WD_EN				BIT(7)
+#define QPNP_PON_RESET_EN			BIT(7)
+#define QPNP_PON_POWER_OFF_MASK			0xF
+#define QPNP_GEN2_POFF_SEQ			BIT(7)
+#define QPNP_GEN2_FAULT_SEQ			BIT(6)
+#define QPNP_GEN2_S3_RESET_SEQ			BIT(5)
+
+#define QPNP_PON_S3_SRC_KPDPWR			0
+#define QPNP_PON_S3_SRC_RESIN			1
+#define QPNP_PON_S3_SRC_KPDPWR_AND_RESIN	2
+#define QPNP_PON_S3_SRC_KPDPWR_OR_RESIN		3
+#define QPNP_PON_S3_SRC_MASK			0x3
+#define QPNP_PON_HARD_RESET_MASK		GENMASK(7, 5)
+
+#define QPNP_PON_UVLO_DLOAD_EN			BIT(7)
+#define QPNP_PON_SMPL_EN			BIT(7)
+
+/* Ranges */
+#define QPNP_PON_S1_TIMER_MAX			10256
+#define QPNP_PON_S2_TIMER_MAX			2000
+#define QPNP_PON_S3_TIMER_SECS_MAX		128
+#define QPNP_PON_S3_DBC_DELAY_MASK		0x07
+#define QPNP_PON_RESET_TYPE_MAX			0xF
+#define PON_S1_COUNT_MAX			0xF
+#define QPNP_PON_MIN_DBC_US			(USEC_PER_SEC / 64)
+#define QPNP_PON_MAX_DBC_US			(USEC_PER_SEC * 2)
+#define QPNP_PON_GEN2_MIN_DBC_US		62
+#define QPNP_PON_GEN2_MAX_DBC_US		(USEC_PER_SEC / 4)
+
+#define QPNP_KEY_STATUS_DELAY			msecs_to_jiffies(250)
+
+#define QPNP_PON_BUFFER_SIZE			9
+
+#define QPNP_POFF_REASON_UVLO			13
+
+enum qpnp_pon_version {
+	QPNP_PON_GEN1_V1,
+	QPNP_PON_GEN1_V2,
+	QPNP_PON_GEN2,
+};
+
+enum pon_type {
+	PON_KPDPWR,
+	PON_RESIN,
+	PON_CBLPWR,
+	PON_KPDPWR_RESIN,
+};
+
+struct qpnp_pon_config {
+	u32 pon_type;
+	u32 support_reset;
+	u32 key_code;
+	u32 s1_timer;
+	u32 s2_timer;
+	u32 s2_type;
+	u32 pull_up;
+	u32 state_irq;
+	u32 bark_irq;
+	u16 s2_cntl_addr;
+	u16 s2_cntl2_addr;
+	bool old_state;
+	bool use_bark;
+	bool config_reset;
+};
+
+struct pon_regulator {
+	struct qpnp_pon		*pon;
+	struct regulator_dev	*rdev;
+	struct regulator_desc	rdesc;
+	u32			addr;
+	u32			bit;
+	bool			enabled;
+};
+
+struct qpnp_pon {
+	struct platform_device	*pdev;
+	struct regmap		*regmap;
+	struct input_dev	*pon_input;
+	struct qpnp_pon_config	*pon_cfg;
+	struct pon_regulator	*pon_reg_cfg;
+	struct list_head	list;
+	struct delayed_work	bark_work;
+	struct dentry		*debugfs;
+	int			pon_trigger_reason;
+	int			pon_power_off_reason;
+	int			num_pon_reg;
+	int			num_pon_config;
+	u32			dbc;
+	u32			uvlo;
+	int			warm_reset_poff_type;
+	int			hard_reset_poff_type;
+	int			shutdown_poff_type;
+	u16			base;
+	u8			subtype;
+	u8			pon_ver;
+	u8			warm_reset_reason1;
+	u8			warm_reset_reason2;
+	bool			is_spon;
+	bool			store_hard_reset_reason;
+};
+
+static int pon_ship_mode_en;
+module_param_named(
+	ship_mode_en, pon_ship_mode_en, int, 0600
+);
+
+static struct qpnp_pon *sys_reset_dev;
+static DEFINE_SPINLOCK(spon_list_slock);
+static LIST_HEAD(spon_dev_list);
+
+static u32 s1_delay[PON_S1_COUNT_MAX + 1] = {
+	0, 32, 56, 80, 138, 184, 272, 408, 608, 904, 1352, 2048,
+	3072, 4480, 6720, 10256
+};
+
+static const char * const qpnp_pon_reason[] = {
+	[0] = "Triggered from Hard Reset",
+	[1] = "Triggered from SMPL (sudden momentary power loss)",
+	[2] = "Triggered from RTC (RTC alarm expiry)",
+	[3] = "Triggered from DC (DC charger insertion)",
+	[4] = "Triggered from USB (USB charger insertion)",
+	[5] = "Triggered from PON1 (secondary PMIC)",
+	[6] = "Triggered from CBL (external power supply)",
+	[7] = "Triggered from KPD (power key press)",
+};
+
+#define POFF_REASON_FAULT_OFFSET	16
+#define POFF_REASON_S3_RESET_OFFSET	32
+static const char * const qpnp_poff_reason[] = {
+	/* QPNP_PON_GEN1 POFF reasons */
+	[0] = "Triggered from SOFT (Software)",
+	[1] = "Triggered from PS_HOLD (PS_HOLD/MSM controlled shutdown)",
+	[2] = "Triggered from PMIC_WD (PMIC watchdog)",
+	[3] = "Triggered from GP1 (Keypad_Reset1)",
+	[4] = "Triggered from GP2 (Keypad_Reset2)",
+	[5] = "Triggered from KPDPWR_AND_RESIN (Simultaneous power key and reset line)",
+	[6] = "Triggered from RESIN_N (Reset line/Volume Down Key)",
+	[7] = "Triggered from KPDPWR_N (Long Power Key hold)",
+	[8] = "N/A",
+	[9] = "N/A",
+	[10] = "N/A",
+	[11] = "Triggered from CHARGER (Charger ENUM_TIMER, BOOT_DONE)",
+	[12] = "Triggered from TFT (Thermal Fault Tolerance)",
+	[13] = "Triggered from UVLO (Under Voltage Lock Out)",
+	[14] = "Triggered from OTST3 (Overtemp)",
+	[15] = "Triggered from STAGE3 (Stage 3 reset)",
+
+	/* QPNP_PON_GEN2 FAULT reasons */
+	[16] = "Triggered from GP_FAULT0",
+	[17] = "Triggered from GP_FAULT1",
+	[18] = "Triggered from GP_FAULT2",
+	[19] = "Triggered from GP_FAULT3",
+	[20] = "Triggered from MBG_FAULT",
+	[21] = "Triggered from OVLO (Over Voltage Lock Out)",
+	[22] = "Triggered from UVLO (Under Voltage Lock Out)",
+	[23] = "Triggered from AVDD_RB",
+	[24] = "N/A",
+	[25] = "N/A",
+	[26] = "N/A",
+	[27] = "Triggered from FAULT_FAULT_N",
+	[28] = "Triggered from FAULT_PBS_WATCHDOG_TO",
+	[29] = "Triggered from FAULT_PBS_NACK",
+	[30] = "Triggered from FAULT_RESTART_PON",
+	[31] = "Triggered from OTST3 (Overtemp)",
+
+	/* QPNP_PON_GEN2 S3_RESET reasons */
+	[32] = "N/A",
+	[33] = "N/A",
+	[34] = "N/A",
+	[35] = "N/A",
+	[36] = "Triggered from S3_RESET_FAULT_N",
+	[37] = "Triggered from S3_RESET_PBS_WATCHDOG_TO",
+	[38] = "Triggered from S3_RESET_PBS_NACK",
+	[39] = "Triggered from S3_RESET_KPDPWR_ANDOR_RESIN (power key and/or reset line)",
+};
+
+static int
+qpnp_pon_masked_write(struct qpnp_pon *pon, u16 addr, u8 mask, u8 val)
+{
+	int rc;
+
+	rc = regmap_update_bits(pon->regmap, addr, mask, val);
+	if (rc)
+		dev_err(&pon->pdev->dev,
+			"Unable to regmap_update_bits to addr=%hx, rc(%d)\n",
+			addr, rc);
+	return rc;
+}
+
+static bool is_pon_gen1(struct qpnp_pon *pon)
+{
+	return pon->subtype == PON_PRIMARY ||
+			pon->subtype == PON_SECONDARY;
+}
+
+static bool is_pon_gen2(struct qpnp_pon *pon)
+{
+	return pon->subtype == PON_GEN2_PRIMARY ||
+			pon->subtype == PON_GEN2_SECONDARY;
+}
+
+/**
+ * qpnp_pon_set_restart_reason - Store device restart reason in PMIC register.
+ *
+ * Returns = 0 if PMIC feature is not available or store restart reason
+ * successfully.
+ * Returns > 0 for errors
+ *
+ * This function is used to store device restart reason in PMIC register.
+ * It checks here to see if the restart reason register has been specified.
+ * If it hasn't, this function should immediately return 0
+ */
+int qpnp_pon_set_restart_reason(enum pon_restart_reason reason)
+{
+	int rc = 0;
+	struct qpnp_pon *pon = sys_reset_dev;
+
+	if (!pon)
+		return 0;
+
+	if (!pon->store_hard_reset_reason)
+		return 0;
+
+	if (is_pon_gen2(pon))
+		rc = qpnp_pon_masked_write(pon, QPNP_PON_SOFT_RB_SPARE(pon),
+					   GENMASK(7, 1), (reason << 1));
+	else
+		rc = qpnp_pon_masked_write(pon, QPNP_PON_SOFT_RB_SPARE(pon),
+					   GENMASK(7, 2), (reason << 2));
+
+	if (rc)
+		dev_err(&pon->pdev->dev,
+				"Unable to write to addr=%x, rc(%d)\n",
+				QPNP_PON_SOFT_RB_SPARE(pon), rc);
+	return rc;
+}
+EXPORT_SYMBOL(qpnp_pon_set_restart_reason);
+
+/*
+ * qpnp_pon_check_hard_reset_stored - Checks if the PMIC need to
+ * store hard reset reason.
+ *
+ * Returns true if reset reason can be stored, false if it cannot be stored
+ *
+ */
+bool qpnp_pon_check_hard_reset_stored(void)
+{
+	struct qpnp_pon *pon = sys_reset_dev;
+
+	if (!pon)
+		return false;
+
+	return pon->store_hard_reset_reason;
+}
+EXPORT_SYMBOL(qpnp_pon_check_hard_reset_stored);
+
+static int qpnp_pon_set_dbc(struct qpnp_pon *pon, u32 delay)
+{
+	int rc = 0;
+	u32 val;
+
+	if (delay == pon->dbc)
+		goto out;
+
+	if (pon->pon_input)
+		mutex_lock(&pon->pon_input->mutex);
+
+	if (is_pon_gen2(pon)) {
+		if (delay < QPNP_PON_GEN2_MIN_DBC_US)
+			delay = QPNP_PON_GEN2_MIN_DBC_US;
+		else if (delay > QPNP_PON_GEN2_MAX_DBC_US)
+			delay = QPNP_PON_GEN2_MAX_DBC_US;
+		val = (delay << QPNP_PON_GEN2_DELAY_BIT_SHIFT) / USEC_PER_SEC;
+	} else {
+		if (delay < QPNP_PON_MIN_DBC_US)
+			delay = QPNP_PON_MIN_DBC_US;
+		else if (delay > QPNP_PON_MAX_DBC_US)
+			delay = QPNP_PON_MAX_DBC_US;
+		val = (delay << QPNP_PON_DELAY_BIT_SHIFT) / USEC_PER_SEC;
+	}
+
+	val = ilog2(val);
+	rc = qpnp_pon_masked_write(pon, QPNP_PON_DBC_CTL(pon),
+					QPNP_PON_DBC_DELAY_MASK(pon), val);
+	if (rc) {
+		dev_err(&pon->pdev->dev, "Unable to set PON debounce\n");
+		goto unlock;
+	}
+
+	pon->dbc = delay;
+
+unlock:
+	if (pon->pon_input)
+		mutex_unlock(&pon->pon_input->mutex);
+out:
+	return rc;
+}
+
+static ssize_t qpnp_pon_dbc_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct qpnp_pon *pon = dev_get_drvdata(dev);
+
+	return snprintf(buf, QPNP_PON_BUFFER_SIZE, "%d\n", pon->dbc);
+}
+
+static ssize_t qpnp_pon_dbc_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	struct qpnp_pon *pon = dev_get_drvdata(dev);
+	u32 value;
+	int rc;
+
+	if (size > QPNP_PON_BUFFER_SIZE)
+		return -EINVAL;
+
+	rc = kstrtou32(buf, 10, &value);
+	if (rc)
+		return rc;
+
+	rc = qpnp_pon_set_dbc(pon, value);
+	if (rc < 0)
+		return rc;
+
+	return size;
+}
+
+static DEVICE_ATTR(debounce_us, 0664, qpnp_pon_dbc_show, qpnp_pon_dbc_store);
+
+static int qpnp_pon_reset_config(struct qpnp_pon *pon,
+		enum pon_power_off_type type)
+{
+	int rc;
+	u16 rst_en_reg;
+
+	if (pon->pon_ver == QPNP_PON_GEN1_V1)
+		rst_en_reg = QPNP_PON_PS_HOLD_RST_CTL(pon);
+	else
+		rst_en_reg = QPNP_PON_PS_HOLD_RST_CTL2(pon);
+
+	/*
+	 * Based on the poweroff type set for a PON device through device tree
+	 * change the type being configured into PS_HOLD_RST_CTL.
+	 */
+	switch (type) {
+	case PON_POWER_OFF_WARM_RESET:
+		if (pon->warm_reset_poff_type != -EINVAL)
+			type = pon->warm_reset_poff_type;
+		break;
+	case PON_POWER_OFF_HARD_RESET:
+		if (pon->hard_reset_poff_type != -EINVAL)
+			type = pon->hard_reset_poff_type;
+		break;
+	case PON_POWER_OFF_SHUTDOWN:
+		if (pon->shutdown_poff_type != -EINVAL)
+			type = pon->shutdown_poff_type;
+		break;
+	default:
+		break;
+	}
+
+	rc = qpnp_pon_masked_write(pon, rst_en_reg, QPNP_PON_RESET_EN, 0);
+	if (rc)
+		dev_err(&pon->pdev->dev,
+			"Unable to write to addr=%hx, rc(%d)\n",
+			rst_en_reg, rc);
+
+	/*
+	 * We need 10 sleep clock cycles here. But since the clock is
+	 * internally generated, we need to add 50% tolerance to be
+	 * conservative.
+	 */
+	udelay(500);
+
+	rc = qpnp_pon_masked_write(pon, QPNP_PON_PS_HOLD_RST_CTL(pon),
+				   QPNP_PON_POWER_OFF_MASK, type);
+	if (rc)
+		dev_err(&pon->pdev->dev,
+			"Unable to write to addr=%x, rc(%d)\n",
+				QPNP_PON_PS_HOLD_RST_CTL(pon), rc);
+
+	rc = qpnp_pon_masked_write(pon, rst_en_reg, QPNP_PON_RESET_EN,
+						    QPNP_PON_RESET_EN);
+	if (rc)
+		dev_err(&pon->pdev->dev,
+			"Unable to write to addr=%hx, rc(%d)\n",
+			rst_en_reg, rc);
+
+	dev_dbg(&pon->pdev->dev, "power off type = 0x%02X\n", type);
+	return rc;
+}
+
+/**
+ * qpnp_pon_system_pwr_off - Configure system-reset PMIC for shutdown or reset
+ * @type: Determines the type of power off to perform - shutdown, reset, etc
+ *
+ * This function will support configuring for multiple PMICs. In some cases, the
+ * PON of secondary PMICs also needs to be configured. So this supports that
+ * requirement. Once the system-reset and secondary PMIC is configured properly,
+ * the MSM can drop PS_HOLD to activate the specified configuration. Note that
+ * this function may be called from atomic context as in the case of the panic
+ * notifier path and thus it should not rely on function calls that may sleep.
+ */
+int qpnp_pon_system_pwr_off(enum pon_power_off_type type)
+{
+	int rc = 0;
+	struct qpnp_pon *pon = sys_reset_dev;
+	struct qpnp_pon *tmp;
+	struct power_supply *batt_psy;
+	union power_supply_propval val;
+	unsigned long flags;
+
+	if (!pon)
+		return -ENODEV;
+
+	rc = qpnp_pon_reset_config(pon, type);
+	if (rc) {
+		dev_err(&pon->pdev->dev,
+			"Error configuring main PON rc: %d\n",
+			rc);
+		return rc;
+	}
+
+	/*
+	 * Check if a secondary PON device needs to be configured. If it
+	 * is available, configure that also as per the requested power off
+	 * type
+	 */
+	spin_lock_irqsave(&spon_list_slock, flags);
+	if (list_empty(&spon_dev_list))
+		goto out;
+
+	list_for_each_entry_safe(pon, tmp, &spon_dev_list, list) {
+		dev_emerg(&pon->pdev->dev,
+				"PMIC@SID%d: configuring PON for reset\n",
+				to_spmi_device(pon->pdev->dev.parent)->usid);
+		rc = qpnp_pon_reset_config(pon, type);
+		if (rc) {
+			dev_err(&pon->pdev->dev,
+				"Error configuring secondary PON rc: %d\n",
+				rc);
+			goto out;
+		}
+	}
+	/* Set ship mode here if it has been requested */
+	if (!!pon_ship_mode_en) {
+		batt_psy = power_supply_get_by_name("battery");
+		if (batt_psy) {
+			pr_debug("Set ship mode!\n");
+			val.intval = 1;
+			rc = power_supply_set_property(batt_psy,
+					POWER_SUPPLY_PROP_SET_SHIP_MODE, &val);
+			if (rc)
+				dev_err(&pon->pdev->dev,
+						"Set ship-mode failed\n");
+		}
+	}
+out:
+	spin_unlock_irqrestore(&spon_list_slock, flags);
+	return rc;
+}
+EXPORT_SYMBOL(qpnp_pon_system_pwr_off);
+
+/**
+ * qpnp_pon_is_warm_reset - Checks if the PMIC went through a warm reset.
+ *
+ * Returns > 0 for warm resets, 0 for not warm reset, < 0 for errors
+ *
+ * Note that this function will only return the warm vs not-warm reset status
+ * of the PMIC that is configured as the system-reset device.
+ */
+int qpnp_pon_is_warm_reset(void)
+{
+	struct qpnp_pon *pon = sys_reset_dev;
+
+	if (!pon)
+		return -EPROBE_DEFER;
+
+	if (is_pon_gen1(pon) || pon->subtype == PON_1REG)
+		return pon->warm_reset_reason1
+			|| (pon->warm_reset_reason2 & QPNP_PON_WARM_RESET_TFT);
+	else
+		return pon->warm_reset_reason1;
+}
+EXPORT_SYMBOL(qpnp_pon_is_warm_reset);
+
+/**
+ * qpnp_pon_wd_config - Disable the wd in a warm reset.
+ * @enable: to enable or disable the PON watch dog
+ *
+ * Returns = 0 for operate successfully, < 0 for errors
+ */
+int qpnp_pon_wd_config(bool enable)
+{
+	struct qpnp_pon *pon = sys_reset_dev;
+	int rc = 0;
+
+	if (!pon)
+		return -EPROBE_DEFER;
+
+	rc = qpnp_pon_masked_write(pon, QPNP_PON_WD_RST_S2_CTL2(pon),
+			QPNP_PON_WD_EN, enable ? QPNP_PON_WD_EN : 0);
+	if (rc)
+		dev_err(&pon->pdev->dev,
+				"Unable to write to addr=%x, rc(%d)\n",
+				QPNP_PON_WD_RST_S2_CTL2(pon), rc);
+
+	return rc;
+}
+EXPORT_SYMBOL(qpnp_pon_wd_config);
+
+static int qpnp_pon_get_trigger_config(enum pon_trigger_source pon_src,
+							bool *enabled)
+{
+	struct qpnp_pon *pon = sys_reset_dev;
+	int rc;
+	u16 addr;
+	int val;
+	u8 mask;
+
+	if (!pon)
+		return -ENODEV;
+
+	if (pon_src < PON_SMPL || pon_src > PON_KPDPWR_N) {
+		dev_err(&pon->pdev->dev, "Invalid PON source\n");
+		return -EINVAL;
+	}
+
+	addr = QPNP_PON_TRIGGER_EN(pon);
+	mask = BIT(pon_src);
+	if (is_pon_gen2(pon) && pon_src == PON_SMPL) {
+		addr = QPNP_PON_SMPL_CTL(pon);
+		mask = QPNP_PON_SMPL_EN;
+	}
+
+
+	rc = regmap_read(pon->regmap, addr, &val);
+	if (rc)
+		dev_err(&pon->pdev->dev,
+			"Unable to read from addr=%hx, rc(%d)\n",
+			addr, rc);
+	else
+		*enabled = !!(val & mask);
+
+	return rc;
+}
+
+/**
+ * qpnp_pon_trigger_config - Configures (enable/disable) the PON trigger source
+ * @pon_src: PON source to be configured
+ * @enable: to enable or disable the PON trigger
+ *
+ * This function configures the power-on trigger capability of a
+ * PON source. If a specific PON trigger is disabled it cannot act
+ * as a power-on source to the PMIC.
+ */
+
+int qpnp_pon_trigger_config(enum pon_trigger_source pon_src, bool enable)
+{
+	struct qpnp_pon *pon = sys_reset_dev;
+	int rc;
+
+	if (!pon)
+		return -EPROBE_DEFER;
+
+	if (pon_src < PON_SMPL || pon_src > PON_KPDPWR_N) {
+		dev_err(&pon->pdev->dev, "Invalid PON source\n");
+		return -EINVAL;
+	}
+
+	if (is_pon_gen2(pon) && pon_src == PON_SMPL) {
+		rc = qpnp_pon_masked_write(pon, QPNP_PON_SMPL_CTL(pon),
+			QPNP_PON_SMPL_EN, enable ? QPNP_PON_SMPL_EN : 0);
+		if (rc)
+			dev_err(&pon->pdev->dev,
+				"Unable to write to addr=%x, rc(%d)\n",
+				QPNP_PON_SMPL_CTL(pon), rc);
+	} else {
+		rc = qpnp_pon_masked_write(pon, QPNP_PON_TRIGGER_EN(pon),
+				BIT(pon_src), enable ? BIT(pon_src) : 0);
+		if (rc)
+			dev_err(&pon->pdev->dev,
+				"Unable to write to addr=%x, rc(%d)\n",
+				QPNP_PON_TRIGGER_EN(pon), rc);
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(qpnp_pon_trigger_config);
+
+/*
+ * This function stores the PMIC warm reset reason register values. It also
+ * clears these registers if the qcom,clear-warm-reset device tree property
+ * is specified.
+ */
+static int qpnp_pon_store_and_clear_warm_reset(struct qpnp_pon *pon)
+{
+	int rc;
+	u8 reg = 0;
+	uint val;
+
+	rc = regmap_read(pon->regmap, QPNP_PON_WARM_RESET_REASON1(pon),
+			 &val);
+	if (rc) {
+		dev_err(&pon->pdev->dev, "Unable to read addr=%x, rc(%d)\n",
+			QPNP_PON_WARM_RESET_REASON1(pon), rc);
+		return rc;
+	}
+	pon->warm_reset_reason1 = (u8)val;
+
+	if (is_pon_gen1(pon) || pon->subtype == PON_1REG) {
+		rc = regmap_read(pon->regmap, QPNP_PON_WARM_RESET_REASON2(pon),
+				 &val);
+		if (rc) {
+			dev_err(&pon->pdev->dev,
+				"Unable to read addr=%x, rc(%d)\n",
+				QPNP_PON_WARM_RESET_REASON2(pon), rc);
+			return rc;
+		}
+	pon->warm_reset_reason2 = (u8)val;
+	}
+
+	if (of_property_read_bool(pon->pdev->dev.of_node,
+					"qcom,clear-warm-reset")) {
+		rc = regmap_write(pon->regmap,
+				  QPNP_PON_WARM_RESET_REASON1(pon), reg);
+		if (rc)
+			dev_err(&pon->pdev->dev,
+				"Unable to write to addr=%hx, rc(%d)\n",
+				QPNP_PON_WARM_RESET_REASON1(pon), rc);
+	}
+
+	return 0;
+}
+
+static struct qpnp_pon_config *
+qpnp_get_cfg(struct qpnp_pon *pon, u32 pon_type)
+{
+	int i;
+
+	for (i = 0; i < pon->num_pon_config; i++) {
+		if (pon_type == pon->pon_cfg[i].pon_type)
+			return  &pon->pon_cfg[i];
+	}
+
+	return NULL;
+}
+
+static int
+qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type)
+{
+	int rc;
+	struct qpnp_pon_config *cfg = NULL;
+	u8  pon_rt_bit = 0;
+	u32 key_status;
+	uint pon_rt_sts;
+
+	cfg = qpnp_get_cfg(pon, pon_type);
+	if (!cfg)
+		return -EINVAL;
+
+	/* Check if key reporting is supported */
+	if (!cfg->key_code)
+		return 0;
+
+	/* check the RT status to get the current status of the line */
+	rc = regmap_read(pon->regmap, QPNP_PON_RT_STS(pon), &pon_rt_sts);
+	if (rc) {
+		dev_err(&pon->pdev->dev, "Unable to read PON RT status\n");
+		return rc;
+	}
+
+	switch (cfg->pon_type) {
+	case PON_KPDPWR:
+		pon_rt_bit = QPNP_PON_KPDPWR_N_SET;
+		break;
+	case PON_RESIN:
+		pon_rt_bit = QPNP_PON_RESIN_N_SET;
+		break;
+	case PON_CBLPWR:
+		pon_rt_bit = QPNP_PON_CBLPWR_N_SET;
+		break;
+	case PON_KPDPWR_RESIN:
+		pon_rt_bit = QPNP_PON_KPDPWR_RESIN_BARK_N_SET;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	pr_debug("PMIC input: code=%d, sts=0x%hhx\n",
+					cfg->key_code, pon_rt_sts);
+	key_status = pon_rt_sts & pon_rt_bit;
+
+	/*
+	 * simulate press event in case release event occurred
+	 * without a press event
+	 */
+	if (!cfg->old_state && !key_status) {
+		input_report_key(pon->pon_input, cfg->key_code, 1);
+		input_sync(pon->pon_input);
+	}
+
+	input_report_key(pon->pon_input, cfg->key_code, key_status);
+	input_sync(pon->pon_input);
+
+	cfg->old_state = !!key_status;
+
+	return 0;
+}
+
+static irqreturn_t qpnp_kpdpwr_irq(int irq, void *_pon)
+{
+	int rc;
+	struct qpnp_pon *pon = _pon;
+
+	rc = qpnp_pon_input_dispatch(pon, PON_KPDPWR);
+	if (rc)
+		dev_err(&pon->pdev->dev, "Unable to send input event\n");
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t qpnp_kpdpwr_bark_irq(int irq, void *_pon)
+{
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t qpnp_resin_irq(int irq, void *_pon)
+{
+	int rc;
+	struct qpnp_pon *pon = _pon;
+
+	rc = qpnp_pon_input_dispatch(pon, PON_RESIN);
+	if (rc)
+		dev_err(&pon->pdev->dev, "Unable to send input event\n");
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t qpnp_kpdpwr_resin_bark_irq(int irq, void *_pon)
+{
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t qpnp_cblpwr_irq(int irq, void *_pon)
+{
+	int rc;
+	struct qpnp_pon *pon = _pon;
+
+	rc = qpnp_pon_input_dispatch(pon, PON_CBLPWR);
+	if (rc)
+		dev_err(&pon->pdev->dev, "Unable to send input event\n");
+
+	return IRQ_HANDLED;
+}
+
+static void print_pon_reg(struct qpnp_pon *pon, u16 offset)
+{
+	int rc;
+	u16 addr;
+	uint reg;
+
+	addr = pon->base + offset;
+	rc = regmap_read(pon->regmap, addr, &reg);
+	if (rc)
+		dev_emerg(&pon->pdev->dev,
+				"Unable to read reg at 0x%04hx\n", addr);
+	else
+		dev_emerg(&pon->pdev->dev, "reg@0x%04hx: %02hhx\n", addr, reg);
+}
+
+#define PON_PBL_STATUS			0x7
+#define PON_PON_REASON1(subtype)	PON_OFFSET(subtype, 0x8, 0xC0)
+#define PON_PON_REASON2			0x9
+#define PON_WARM_RESET_REASON1(subtype)	PON_OFFSET(subtype, 0xA, 0xC2)
+#define PON_WARM_RESET_REASON2		0xB
+#define PON_POFF_REASON1(subtype)	PON_OFFSET(subtype, 0xC, 0xC5)
+#define PON_POFF_REASON2		0xD
+#define PON_SOFT_RESET_REASON1(subtype)	PON_OFFSET(subtype, 0xE, 0xCB)
+#define PON_SOFT_RESET_REASON2		0xF
+#define PON_FAULT_REASON1		0xC8
+#define PON_FAULT_REASON2		0xC9
+#define PON_PMIC_WD_RESET_S1_TIMER	0x54
+#define PON_PMIC_WD_RESET_S2_TIMER	0x55
+static irqreturn_t qpnp_pmic_wd_bark_irq(int irq, void *_pon)
+{
+	struct qpnp_pon *pon = _pon;
+
+	print_pon_reg(pon, PON_PBL_STATUS);
+	print_pon_reg(pon, PON_PON_REASON1(pon->subtype));
+	print_pon_reg(pon, PON_WARM_RESET_REASON1(pon->subtype));
+	print_pon_reg(pon, PON_SOFT_RESET_REASON1(pon->subtype));
+	print_pon_reg(pon, PON_POFF_REASON1(pon->subtype));
+	if (is_pon_gen1(pon) || pon->subtype == PON_1REG) {
+		print_pon_reg(pon, PON_PON_REASON2);
+		print_pon_reg(pon, PON_WARM_RESET_REASON2);
+		print_pon_reg(pon, PON_POFF_REASON2);
+		print_pon_reg(pon, PON_SOFT_RESET_REASON2);
+	} else {
+		print_pon_reg(pon, PON_FAULT_REASON1);
+		print_pon_reg(pon, PON_FAULT_REASON2);
+	}
+	print_pon_reg(pon, PON_PMIC_WD_RESET_S1_TIMER);
+	print_pon_reg(pon, PON_PMIC_WD_RESET_S2_TIMER);
+	panic("PMIC Watch dog triggered");
+
+	return IRQ_HANDLED;
+}
+
+static void bark_work_func(struct work_struct *work)
+{
+	int rc;
+	uint pon_rt_sts = 0;
+	struct qpnp_pon_config *cfg;
+	struct qpnp_pon *pon =
+		container_of(work, struct qpnp_pon, bark_work.work);
+
+	cfg = qpnp_get_cfg(pon, PON_RESIN);
+	if (!cfg) {
+		dev_err(&pon->pdev->dev, "Invalid config pointer\n");
+		goto err_return;
+	}
+
+	/* enable reset */
+	rc = qpnp_pon_masked_write(pon, cfg->s2_cntl2_addr,
+				QPNP_PON_S2_CNTL_EN, QPNP_PON_S2_CNTL_EN);
+	if (rc) {
+		dev_err(&pon->pdev->dev, "Unable to configure S2 enable\n");
+		goto err_return;
+	}
+	/* bark RT status update delay */
+	msleep(100);
+	/* read the bark RT status */
+	rc = regmap_read(pon->regmap, QPNP_PON_RT_STS(pon), &pon_rt_sts);
+	if (rc) {
+		dev_err(&pon->pdev->dev, "Unable to read PON RT status\n");
+		goto err_return;
+	}
+
+	if (!(pon_rt_sts & QPNP_PON_RESIN_BARK_N_SET)) {
+		/* report the key event and enable the bark IRQ */
+		input_report_key(pon->pon_input, cfg->key_code, 0);
+		input_sync(pon->pon_input);
+		enable_irq(cfg->bark_irq);
+	} else {
+		/* disable reset */
+		rc = qpnp_pon_masked_write(pon, cfg->s2_cntl2_addr,
+				QPNP_PON_S2_CNTL_EN, 0);
+		if (rc) {
+			dev_err(&pon->pdev->dev,
+				"Unable to configure S2 enable\n");
+			goto err_return;
+		}
+		/* re-arm the work */
+		schedule_delayed_work(&pon->bark_work, QPNP_KEY_STATUS_DELAY);
+	}
+
+err_return:
+	return;
+}
+
+static irqreturn_t qpnp_resin_bark_irq(int irq, void *_pon)
+{
+	int rc;
+	struct qpnp_pon *pon = _pon;
+	struct qpnp_pon_config *cfg;
+
+	/* disable the bark interrupt */
+	disable_irq_nosync(irq);
+
+	cfg = qpnp_get_cfg(pon, PON_RESIN);
+	if (!cfg) {
+		dev_err(&pon->pdev->dev, "Invalid config pointer\n");
+		goto err_exit;
+	}
+
+	/* disable reset */
+	rc = qpnp_pon_masked_write(pon, cfg->s2_cntl2_addr,
+					QPNP_PON_S2_CNTL_EN, 0);
+	if (rc) {
+		dev_err(&pon->pdev->dev, "Unable to configure S2 enable\n");
+		goto err_exit;
+	}
+
+	/* report the key event */
+	input_report_key(pon->pon_input, cfg->key_code, 1);
+	input_sync(pon->pon_input);
+	/* schedule work to check the bark status for key-release */
+	schedule_delayed_work(&pon->bark_work, QPNP_KEY_STATUS_DELAY);
+err_exit:
+	return IRQ_HANDLED;
+}
+
+static int
+qpnp_config_pull(struct qpnp_pon *pon, struct qpnp_pon_config *cfg)
+{
+	int rc;
+	u8 pull_bit;
+
+	switch (cfg->pon_type) {
+	case PON_KPDPWR:
+		pull_bit = QPNP_PON_KPDPWR_PULL_UP;
+		break;
+	case PON_RESIN:
+		pull_bit = QPNP_PON_RESIN_PULL_UP;
+		break;
+	case PON_CBLPWR:
+		pull_bit = QPNP_PON_CBLPWR_PULL_UP;
+		break;
+	case PON_KPDPWR_RESIN:
+		pull_bit = QPNP_PON_KPDPWR_PULL_UP | QPNP_PON_RESIN_PULL_UP;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	rc = qpnp_pon_masked_write(pon, QPNP_PON_PULL_CTL(pon),
+				pull_bit, cfg->pull_up ? pull_bit : 0);
+	if (rc)
+		dev_err(&pon->pdev->dev, "Unable to config pull-up\n");
+
+	return rc;
+}
+
+static int
+qpnp_config_reset(struct qpnp_pon *pon, struct qpnp_pon_config *cfg)
+{
+	int rc;
+	u8 i;
+	u16 s1_timer_addr, s2_timer_addr;
+
+	switch (cfg->pon_type) {
+	case PON_KPDPWR:
+		s1_timer_addr = QPNP_PON_KPDPWR_S1_TIMER(pon);
+		s2_timer_addr = QPNP_PON_KPDPWR_S2_TIMER(pon);
+		break;
+	case PON_RESIN:
+		s1_timer_addr = QPNP_PON_RESIN_S1_TIMER(pon);
+		s2_timer_addr = QPNP_PON_RESIN_S2_TIMER(pon);
+		break;
+	case PON_KPDPWR_RESIN:
+		s1_timer_addr = QPNP_PON_KPDPWR_RESIN_S1_TIMER(pon);
+		s2_timer_addr = QPNP_PON_KPDPWR_RESIN_S2_TIMER(pon);
+		break;
+	default:
+		return -EINVAL;
+	}
+	/* disable S2 reset */
+	rc = qpnp_pon_masked_write(pon, cfg->s2_cntl2_addr,
+				QPNP_PON_S2_CNTL_EN, 0);
+	if (rc) {
+		dev_err(&pon->pdev->dev, "Unable to configure S2 enable\n");
+		return rc;
+	}
+
+	usleep_range(100, 120);
+
+	/* configure s1 timer, s2 timer and reset type */
+	for (i = 0; i < PON_S1_COUNT_MAX + 1; i++) {
+		if (cfg->s1_timer <= s1_delay[i])
+			break;
+	}
+	rc = qpnp_pon_masked_write(pon, s1_timer_addr,
+				QPNP_PON_S1_TIMER_MASK, i);
+	if (rc) {
+		dev_err(&pon->pdev->dev, "Unable to configure S1 timer\n");
+		return rc;
+	}
+
+	i = 0;
+	if (cfg->s2_timer) {
+		i = cfg->s2_timer / 10;
+		i = ilog2(i + 1);
+	}
+
+	rc = qpnp_pon_masked_write(pon, s2_timer_addr,
+				QPNP_PON_S2_TIMER_MASK, i);
+	if (rc) {
+		dev_err(&pon->pdev->dev, "Unable to configure S2 timer\n");
+		return rc;
+	}
+
+	rc = qpnp_pon_masked_write(pon, cfg->s2_cntl_addr,
+				QPNP_PON_S2_CNTL_TYPE_MASK, (u8)cfg->s2_type);
+	if (rc) {
+		dev_err(&pon->pdev->dev,
+			"Unable to configure S2 reset type\n");
+		return rc;
+	}
+
+	/* enable S2 reset */
+	rc = qpnp_pon_masked_write(pon, cfg->s2_cntl2_addr,
+				QPNP_PON_S2_CNTL_EN, QPNP_PON_S2_CNTL_EN);
+	if (rc) {
+		dev_err(&pon->pdev->dev, "Unable to configure S2 enable\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+static int
+qpnp_pon_request_irqs(struct qpnp_pon *pon, struct qpnp_pon_config *cfg)
+{
+	int rc = 0;
+
+	switch (cfg->pon_type) {
+	case PON_KPDPWR:
+		rc = devm_request_irq(&pon->pdev->dev, cfg->state_irq,
+							qpnp_kpdpwr_irq,
+				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+						"qpnp_kpdpwr_status", pon);
+		if (rc < 0) {
+			dev_err(&pon->pdev->dev, "Can't request %d IRQ\n",
+							cfg->state_irq);
+			return rc;
+		}
+		if (cfg->use_bark) {
+			rc = devm_request_irq(&pon->pdev->dev, cfg->bark_irq,
+						qpnp_kpdpwr_bark_irq,
+						IRQF_TRIGGER_RISING,
+						"qpnp_kpdpwr_bark", pon);
+			if (rc < 0) {
+				dev_err(&pon->pdev->dev,
+					"Can't request %d IRQ\n",
+						cfg->bark_irq);
+				return rc;
+			}
+		}
+		break;
+	case PON_RESIN:
+		rc = devm_request_irq(&pon->pdev->dev, cfg->state_irq,
+							qpnp_resin_irq,
+				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+						"qpnp_resin_status", pon);
+		if (rc < 0) {
+			dev_err(&pon->pdev->dev, "Can't request %d IRQ\n",
+							cfg->state_irq);
+			return rc;
+		}
+		if (cfg->use_bark) {
+			rc = devm_request_irq(&pon->pdev->dev, cfg->bark_irq,
+						qpnp_resin_bark_irq,
+						IRQF_TRIGGER_RISING,
+						"qpnp_resin_bark", pon);
+			if (rc < 0) {
+				dev_err(&pon->pdev->dev,
+					"Can't request %d IRQ\n",
+						cfg->bark_irq);
+				return rc;
+			}
+		}
+		break;
+	case PON_CBLPWR:
+		rc = devm_request_irq(&pon->pdev->dev, cfg->state_irq,
+							qpnp_cblpwr_irq,
+				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+					"qpnp_cblpwr_status", pon);
+		if (rc < 0) {
+			dev_err(&pon->pdev->dev, "Can't request %d IRQ\n",
+							cfg->state_irq);
+			return rc;
+		}
+		break;
+	case PON_KPDPWR_RESIN:
+		if (cfg->use_bark) {
+			rc = devm_request_irq(&pon->pdev->dev, cfg->bark_irq,
+					qpnp_kpdpwr_resin_bark_irq,
+					IRQF_TRIGGER_RISING,
+					"qpnp_kpdpwr_resin_bark", pon);
+			if (rc < 0) {
+				dev_err(&pon->pdev->dev,
+					"Can't request %d IRQ\n",
+						cfg->bark_irq);
+				return rc;
+			}
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* mark the interrupts wakeable if they support linux-key */
+	if (cfg->key_code) {
+		enable_irq_wake(cfg->state_irq);
+		/* special handling for RESIN due to a hardware bug */
+		if (cfg->pon_type == PON_RESIN && cfg->support_reset)
+			enable_irq_wake(cfg->bark_irq);
+	}
+
+	return rc;
+}
+
+static int
+qpnp_pon_config_input(struct qpnp_pon *pon,  struct qpnp_pon_config *cfg)
+{
+	if (!pon->pon_input) {
+		pon->pon_input = input_allocate_device();
+		if (!pon->pon_input) {
+			dev_err(&pon->pdev->dev,
+				"Can't allocate pon input device\n");
+			return -ENOMEM;
+		}
+		pon->pon_input->name = "qpnp_pon";
+		pon->pon_input->phys = "qpnp_pon/input0";
+	}
+
+	input_set_capability(pon->pon_input, EV_KEY, cfg->key_code);
+
+	return 0;
+}
+
+static int qpnp_pon_config_init(struct qpnp_pon *pon)
+{
+	int rc = 0, i = 0, pmic_wd_bark_irq;
+	struct device_node *pp = NULL;
+	struct qpnp_pon_config *cfg;
+	uint pmic_type;
+	uint revid_rev4;
+
+	if (!pon->num_pon_config) {
+		dev_dbg(&pon->pdev->dev, "num_pon_config: %d\n",
+			pon->num_pon_config);
+		return 0;
+	}
+
+	/* iterate through the list of pon configs */
+	for_each_available_child_of_node(pon->pdev->dev.of_node, pp) {
+		if (!of_find_property(pp, "qcom,pon-type", NULL))
+			continue;
+
+		cfg = &pon->pon_cfg[i++];
+
+		rc = of_property_read_u32(pp, "qcom,pon-type", &cfg->pon_type);
+		if (rc) {
+			dev_err(&pon->pdev->dev, "PON type not specified\n");
+			return rc;
+		}
+
+		switch (cfg->pon_type) {
+		case PON_KPDPWR:
+			cfg->state_irq = platform_get_irq_byname(pon->pdev,
+								 "kpdpwr");
+			if (cfg->state_irq < 0) {
+				dev_err(&pon->pdev->dev,
+					"Unable to get kpdpwr irq\n");
+				return cfg->state_irq;
+			}
+
+			rc = of_property_read_u32(pp, "qcom,support-reset",
+							&cfg->support_reset);
+
+			if (rc) {
+				if (rc == -EINVAL) {
+					dev_dbg(&pon->pdev->dev,
+						"'qcom,support-reset' DT property doesn't exist\n");
+				} else {
+					dev_err(&pon->pdev->dev,
+						"Unable to read 'qcom,support-reset'\n");
+					return rc;
+				}
+			} else {
+				cfg->config_reset = true;
+			}
+
+			cfg->use_bark = of_property_read_bool(pp,
+							"qcom,use-bark");
+			if (cfg->use_bark) {
+				cfg->bark_irq
+					= platform_get_irq_byname(pon->pdev,
+								"kpdpwr-bark");
+				if (cfg->bark_irq < 0) {
+					dev_err(&pon->pdev->dev,
+					"Unable to get kpdpwr-bark irq\n");
+					return cfg->bark_irq;
+				}
+			}
+
+			/*
+			 * If the value read from REVISION2 register is 0x00,
+			 * then there is a single register to control s2 reset.
+			 * Otherwise there are separate registers for s2 reset
+			 * type and s2 reset enable.
+			 */
+			if (pon->pon_ver == QPNP_PON_GEN1_V1) {
+				cfg->s2_cntl_addr = cfg->s2_cntl2_addr =
+					QPNP_PON_KPDPWR_S2_CNTL(pon);
+			} else {
+				cfg->s2_cntl_addr =
+					QPNP_PON_KPDPWR_S2_CNTL(pon);
+				cfg->s2_cntl2_addr =
+					QPNP_PON_KPDPWR_S2_CNTL2(pon);
+			}
+
+			break;
+		case PON_RESIN:
+			cfg->state_irq = platform_get_irq_byname(pon->pdev,
+								 "resin");
+			if (cfg->state_irq < 0) {
+				dev_err(&pon->pdev->dev,
+					"Unable to get resin irq\n");
+				return cfg->bark_irq;
+			}
+
+			rc = of_property_read_u32(pp, "qcom,support-reset",
+							&cfg->support_reset);
+
+			if (rc) {
+				if (rc == -EINVAL) {
+					dev_dbg(&pon->pdev->dev,
+						"'qcom,support-reset' DT property doesn't exist\n");
+				} else {
+					dev_err(&pon->pdev->dev,
+						"Unable to read 'qcom,support-reset'\n");
+					return rc;
+				}
+			} else {
+				cfg->config_reset = true;
+			}
+
+			cfg->use_bark = of_property_read_bool(pp,
+							"qcom,use-bark");
+
+			rc = regmap_read(pon->regmap, PMIC_VERSION_REG,
+					 &pmic_type);
+
+			if (rc) {
+				dev_err(&pon->pdev->dev,
+					"Unable to read PMIC type\n");
+				return rc;
+			}
+
+			if (pmic_type == PMIC_VER_8941) {
+
+				rc = regmap_read(pon->regmap,
+						 PMIC_VERSION_REV4_REG,
+						 &revid_rev4);
+
+				if (rc) {
+					dev_err(&pon->pdev->dev,
+					"Unable to read PMIC revision ID\n");
+					return rc;
+				}
+
+				/*
+				 * PM8941 V3 does not have hardware bug. Hence
+				 * bark is not required from PMIC versions 3.0.
+				 */
+				if (!(revid_rev4 == PMIC8941_V1_REV4 ||
+					revid_rev4 == PMIC8941_V2_REV4)) {
+					cfg->support_reset = false;
+					cfg->use_bark = false;
+				}
+			}
+
+			if (cfg->use_bark) {
+				cfg->bark_irq
+					= platform_get_irq_byname(pon->pdev,
+								"resin-bark");
+				if (cfg->bark_irq < 0) {
+					dev_err(&pon->pdev->dev,
+					"Unable to get resin-bark irq\n");
+					return cfg->bark_irq;
+				}
+			}
+
+			if (pon->pon_ver == QPNP_PON_GEN1_V1) {
+				cfg->s2_cntl_addr = cfg->s2_cntl2_addr =
+					QPNP_PON_RESIN_S2_CNTL(pon);
+			} else {
+				cfg->s2_cntl_addr =
+					QPNP_PON_RESIN_S2_CNTL(pon);
+				cfg->s2_cntl2_addr =
+					QPNP_PON_RESIN_S2_CNTL2(pon);
+			}
+
+			break;
+		case PON_CBLPWR:
+			cfg->state_irq = platform_get_irq_byname(pon->pdev,
+								 "cblpwr");
+			if (cfg->state_irq < 0) {
+				dev_err(&pon->pdev->dev,
+						"Unable to get cblpwr irq\n");
+				return rc;
+			}
+			break;
+		case PON_KPDPWR_RESIN:
+			rc = of_property_read_u32(pp, "qcom,support-reset",
+							&cfg->support_reset);
+
+			if (rc) {
+				if (rc == -EINVAL) {
+					dev_dbg(&pon->pdev->dev,
+						"'qcom,support-reset' DT property doesn't exist\n");
+				} else {
+					dev_err(&pon->pdev->dev,
+						"Unable to read 'qcom,support-reset'\n");
+					return rc;
+				}
+			} else {
+				cfg->config_reset = true;
+			}
+
+			cfg->use_bark = of_property_read_bool(pp,
+							"qcom,use-bark");
+			if (cfg->use_bark) {
+				cfg->bark_irq
+					= platform_get_irq_byname(pon->pdev,
+								"kpdpwr-resin-bark");
+				if (cfg->bark_irq < 0) {
+					dev_err(&pon->pdev->dev,
+					"Unable to get kpdpwr-resin-bark irq\n");
+					return cfg->bark_irq;
+				}
+			}
+
+			if (pon->pon_ver == QPNP_PON_GEN1_V1) {
+				cfg->s2_cntl_addr = cfg->s2_cntl2_addr =
+				QPNP_PON_KPDPWR_RESIN_S2_CNTL(pon);
+			} else {
+				cfg->s2_cntl_addr =
+				QPNP_PON_KPDPWR_RESIN_S2_CNTL(pon);
+				cfg->s2_cntl2_addr =
+				QPNP_PON_KPDPWR_RESIN_S2_CNTL2(pon);
+			}
+
+			break;
+		default:
+			dev_err(&pon->pdev->dev, "PON RESET %d not supported",
+								cfg->pon_type);
+			return -EINVAL;
+		}
+
+		if (cfg->support_reset) {
+			/*
+			 * Get the reset parameters (bark debounce time and
+			 * reset debounce time) for the reset line.
+			 */
+			rc = of_property_read_u32(pp, "qcom,s1-timer",
+							&cfg->s1_timer);
+			if (rc) {
+				dev_err(&pon->pdev->dev,
+					"Unable to read s1-timer\n");
+				return rc;
+			}
+			if (cfg->s1_timer > QPNP_PON_S1_TIMER_MAX) {
+				dev_err(&pon->pdev->dev,
+					"Incorrect S1 debounce time\n");
+				return -EINVAL;
+			}
+			rc = of_property_read_u32(pp, "qcom,s2-timer",
+							&cfg->s2_timer);
+			if (rc) {
+				dev_err(&pon->pdev->dev,
+					"Unable to read s2-timer\n");
+				return rc;
+			}
+			if (cfg->s2_timer > QPNP_PON_S2_TIMER_MAX) {
+				dev_err(&pon->pdev->dev,
+					"Incorrect S2 debounce time\n");
+				return -EINVAL;
+			}
+			rc = of_property_read_u32(pp, "qcom,s2-type",
+							&cfg->s2_type);
+			if (rc) {
+				dev_err(&pon->pdev->dev,
+					"Unable to read s2-type\n");
+				return rc;
+			}
+			if (cfg->s2_type > QPNP_PON_RESET_TYPE_MAX) {
+				dev_err(&pon->pdev->dev,
+					"Incorrect reset type specified\n");
+				return -EINVAL;
+			}
+		}
+		/*
+		 * Get the standard-key parameters. This might not be
+		 * specified if there is no key mapping on the reset line.
+		 */
+		rc = of_property_read_u32(pp, "linux,code", &cfg->key_code);
+		if (rc && rc != -EINVAL) {
+			dev_err(&pon->pdev->dev, "Unable to read key-code\n");
+			return rc;
+		}
+		/* Register key configuration */
+		if (cfg->key_code) {
+			rc = qpnp_pon_config_input(pon, cfg);
+			if (rc < 0)
+				return rc;
+		}
+		/* get the pull-up configuration */
+		rc = of_property_read_u32(pp, "qcom,pull-up", &cfg->pull_up);
+		if (rc && rc != -EINVAL) {
+			dev_err(&pon->pdev->dev, "Unable to read pull-up\n");
+			return rc;
+		}
+	}
+
+	pmic_wd_bark_irq = platform_get_irq_byname(pon->pdev, "pmic-wd-bark");
+	/* request the pmic-wd-bark irq only if it is defined */
+	if (pmic_wd_bark_irq >= 0) {
+		rc = devm_request_irq(&pon->pdev->dev, pmic_wd_bark_irq,
+					qpnp_pmic_wd_bark_irq,
+					IRQF_TRIGGER_RISING,
+					"qpnp_pmic_wd_bark", pon);
+		if (rc < 0) {
+			dev_err(&pon->pdev->dev,
+				"Can't request %d IRQ\n",
+					pmic_wd_bark_irq);
+			goto free_input_dev;
+		}
+	}
+
+	/* register the input device */
+	if (pon->pon_input) {
+		rc = input_register_device(pon->pon_input);
+		if (rc) {
+			dev_err(&pon->pdev->dev,
+				"Can't register pon key: %d\n", rc);
+			goto free_input_dev;
+		}
+	}
+
+	for (i = 0; i < pon->num_pon_config; i++) {
+		cfg = &pon->pon_cfg[i];
+		/* Configure the pull-up */
+		rc = qpnp_config_pull(pon, cfg);
+		if (rc) {
+			dev_err(&pon->pdev->dev, "Unable to config pull-up\n");
+			goto unreg_input_dev;
+		}
+
+		if (cfg->config_reset) {
+			/* Configure the reset-configuration */
+			if (cfg->support_reset) {
+				rc = qpnp_config_reset(pon, cfg);
+				if (rc) {
+					dev_err(&pon->pdev->dev,
+						"Unable to config pon reset\n");
+					goto unreg_input_dev;
+				}
+			} else {
+				if (cfg->pon_type != PON_CBLPWR) {
+					/* disable S2 reset */
+					rc = qpnp_pon_masked_write(pon,
+						cfg->s2_cntl2_addr,
+						QPNP_PON_S2_CNTL_EN, 0);
+					if (rc) {
+						dev_err(&pon->pdev->dev,
+							"Unable to disable S2 reset\n");
+						goto unreg_input_dev;
+					}
+				}
+			}
+		}
+
+		rc = qpnp_pon_request_irqs(pon, cfg);
+		if (rc) {
+			dev_err(&pon->pdev->dev, "Unable to request-irq's\n");
+			goto unreg_input_dev;
+		}
+	}
+
+	device_init_wakeup(&pon->pdev->dev, 1);
+
+	return rc;
+
+unreg_input_dev:
+	if (pon->pon_input)
+		input_unregister_device(pon->pon_input);
+free_input_dev:
+	if (pon->pon_input)
+		input_free_device(pon->pon_input);
+	return rc;
+}
+
+static int pon_spare_regulator_enable(struct regulator_dev *rdev)
+{
+	int rc = 0;
+	u8 value;
+	struct pon_regulator *pon_reg = rdev_get_drvdata(rdev);
+
+	pr_debug("reg %s enable addr: %x bit: %d\n", rdev->desc->name,
+		pon_reg->addr, pon_reg->bit);
+
+	value = BIT(pon_reg->bit) & 0xFF;
+	rc = qpnp_pon_masked_write(pon_reg->pon, pon_reg->pon->base +
+				pon_reg->addr, value, value);
+	if (rc)
+		dev_err(&pon_reg->pon->pdev->dev, "Unable to write to %x\n",
+			pon_reg->pon->base + pon_reg->addr);
+	else
+		pon_reg->enabled = true;
+	return rc;
+}
+
+static int pon_spare_regulator_disable(struct regulator_dev *rdev)
+{
+	int rc = 0;
+	u8 mask;
+	struct pon_regulator *pon_reg = rdev_get_drvdata(rdev);
+
+	pr_debug("reg %s disable addr: %x bit: %d\n", rdev->desc->name,
+		pon_reg->addr, pon_reg->bit);
+
+	mask = BIT(pon_reg->bit) & 0xFF;
+	rc = qpnp_pon_masked_write(pon_reg->pon, pon_reg->pon->base +
+				pon_reg->addr, mask, 0);
+	if (rc)
+		dev_err(&pon_reg->pon->pdev->dev, "Unable to write to %x\n",
+			pon_reg->pon->base + pon_reg->addr);
+	else
+		pon_reg->enabled = false;
+	return rc;
+}
+
+static int pon_spare_regulator_is_enable(struct regulator_dev *rdev)
+{
+	struct pon_regulator *pon_reg = rdev_get_drvdata(rdev);
+
+	return pon_reg->enabled;
+}
+
+struct regulator_ops pon_spare_reg_ops = {
+	.enable		= pon_spare_regulator_enable,
+	.disable	= pon_spare_regulator_disable,
+	.is_enabled	= pon_spare_regulator_is_enable,
+};
+
+static int pon_regulator_init(struct qpnp_pon *pon)
+{
+	int rc = 0, i = 0;
+	struct regulator_init_data *init_data;
+	struct regulator_config reg_cfg = {};
+	struct device_node *node = NULL;
+	struct device *dev = &pon->pdev->dev;
+	struct pon_regulator *pon_reg;
+
+	if (!pon->num_pon_reg)
+		return 0;
+
+	pon->pon_reg_cfg = devm_kcalloc(dev, pon->num_pon_reg,
+					sizeof(*(pon->pon_reg_cfg)),
+					GFP_KERNEL);
+
+	if (!pon->pon_reg_cfg)
+		return -ENOMEM;
+
+	for_each_available_child_of_node(dev->of_node, node) {
+		if (!of_find_property(node, "regulator-name", NULL))
+			continue;
+
+		pon_reg = &pon->pon_reg_cfg[i++];
+		pon_reg->pon = pon;
+
+		rc = of_property_read_u32(node, "qcom,pon-spare-reg-addr",
+			&pon_reg->addr);
+		if (rc) {
+			dev_err(dev, "Unable to read address for regulator, rc=%d\n",
+				rc);
+			return rc;
+		}
+
+		rc = of_property_read_u32(node, "qcom,pon-spare-reg-bit",
+			&pon_reg->bit);
+		if (rc) {
+			dev_err(dev, "Unable to read bit for regulator, rc=%d\n",
+				rc);
+			return rc;
+		}
+
+		init_data = of_get_regulator_init_data(dev, node,
+				&pon_reg->rdesc);
+		if (!init_data) {
+			dev_err(dev, "regulator init data is missing\n");
+			return -EINVAL;
+		}
+		init_data->constraints.valid_ops_mask
+			|= REGULATOR_CHANGE_STATUS;
+
+		if (!init_data->constraints.name) {
+			dev_err(dev, "regulator-name is missing\n");
+			return -EINVAL;
+		}
+
+		pon_reg->rdesc.owner = THIS_MODULE;
+		pon_reg->rdesc.type = REGULATOR_VOLTAGE;
+		pon_reg->rdesc.ops = &pon_spare_reg_ops;
+		pon_reg->rdesc.name = init_data->constraints.name;
+
+		reg_cfg.dev = dev;
+		reg_cfg.init_data = init_data;
+		reg_cfg.driver_data = pon_reg;
+		reg_cfg.of_node = node;
+
+		pon_reg->rdev = regulator_register(&pon_reg->rdesc, &reg_cfg);
+		if (IS_ERR(pon_reg->rdev)) {
+			rc = PTR_ERR(pon_reg->rdev);
+			pon_reg->rdev = NULL;
+			if (rc != -EPROBE_DEFER)
+				dev_err(dev, "regulator_register failed, rc=%d\n",
+					rc);
+			return rc;
+		}
+	}
+	return rc;
+}
+
+static bool smpl_en;
+
+static int qpnp_pon_smpl_en_get(char *buf, const struct kernel_param *kp)
+{
+	bool enabled = 0;
+	int rc;
+
+	rc = qpnp_pon_get_trigger_config(PON_SMPL, &enabled);
+	if (rc < 0)
+		return rc;
+
+	return snprintf(buf, QPNP_PON_BUFFER_SIZE, "%d", enabled);
+}
+
+static int qpnp_pon_smpl_en_set(const char *val,
+					const struct kernel_param *kp)
+{
+	int rc;
+
+	rc = param_set_bool(val, kp);
+	if (rc < 0) {
+		pr_err("Unable to set smpl_en rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_pon_trigger_config(PON_SMPL, *(bool *)kp->arg);
+	return rc;
+}
+
+static struct kernel_param_ops smpl_en_ops = {
+	.set = qpnp_pon_smpl_en_set,
+	.get = qpnp_pon_smpl_en_get,
+};
+
+module_param_cb(smpl_en, &smpl_en_ops, &smpl_en, 0644);
+
+static bool dload_on_uvlo;
+
+static int qpnp_pon_debugfs_uvlo_dload_get(char *buf,
+		const struct kernel_param *kp)
+{
+	struct qpnp_pon *pon = sys_reset_dev;
+	int rc = 0;
+	uint reg;
+
+	if (!pon)
+		return -ENODEV;
+
+	rc = regmap_read(pon->regmap, QPNP_PON_XVDD_RB_SPARE(pon), &reg);
+	if (rc) {
+		dev_err(&pon->pdev->dev,
+			"Unable to read addr=%x, rc(%d)\n",
+			QPNP_PON_XVDD_RB_SPARE(pon), rc);
+		return rc;
+	}
+
+	return snprintf(buf, PAGE_SIZE, "%d",
+			!!(QPNP_PON_UVLO_DLOAD_EN & reg));
+}
+
+static int qpnp_pon_debugfs_uvlo_dload_set(const char *val,
+		const struct kernel_param *kp)
+{
+	struct qpnp_pon *pon = sys_reset_dev;
+	int rc = 0;
+	uint reg;
+
+	if (!pon)
+		return -ENODEV;
+
+	rc = param_set_bool(val, kp);
+	if (rc) {
+		pr_err("Unable to set bms_reset: %d\n", rc);
+		return rc;
+	}
+
+	rc = regmap_read(pon->regmap, QPNP_PON_XVDD_RB_SPARE(pon), &reg);
+	if (rc) {
+		dev_err(&pon->pdev->dev,
+			"Unable to read addr=%x, rc(%d)\n",
+			QPNP_PON_XVDD_RB_SPARE(pon), rc);
+		return rc;
+	}
+
+	reg &= ~QPNP_PON_UVLO_DLOAD_EN;
+	if (*(bool *)kp->arg)
+		reg |= QPNP_PON_UVLO_DLOAD_EN;
+
+	rc = regmap_write(pon->regmap, QPNP_PON_XVDD_RB_SPARE(pon), reg);
+	if (rc) {
+		dev_err(&pon->pdev->dev,
+			"Unable to write to addr=%hx, rc(%d)\n",
+				QPNP_PON_XVDD_RB_SPARE(pon), rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static struct kernel_param_ops dload_on_uvlo_ops = {
+	.set = qpnp_pon_debugfs_uvlo_dload_set,
+	.get = qpnp_pon_debugfs_uvlo_dload_get,
+};
+
+module_param_cb(dload_on_uvlo, &dload_on_uvlo_ops, &dload_on_uvlo, 0644);
+
+#if defined(CONFIG_DEBUG_FS)
+
+static int qpnp_pon_debugfs_uvlo_get(void *data, u64 *val)
+{
+	struct qpnp_pon *pon = (struct qpnp_pon *) data;
+
+	*val = pon->uvlo;
+
+	return 0;
+}
+
+static int qpnp_pon_debugfs_uvlo_set(void *data, u64 val)
+{
+	struct qpnp_pon *pon = (struct qpnp_pon *) data;
+
+	if (pon->pon_trigger_reason == PON_SMPL ||
+		pon->pon_power_off_reason == QPNP_POFF_REASON_UVLO)
+		panic("An UVLO was occurred.\n");
+	pon->uvlo = val;
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(qpnp_pon_debugfs_uvlo_fops, qpnp_pon_debugfs_uvlo_get,
+			qpnp_pon_debugfs_uvlo_set, "0x%02llx\n");
+
+static void qpnp_pon_debugfs_init(struct platform_device *pdev)
+{
+	struct qpnp_pon *pon = dev_get_drvdata(&pdev->dev);
+	struct dentry *ent;
+
+	pon->debugfs = debugfs_create_dir(dev_name(&pdev->dev), NULL);
+	if (!pon->debugfs) {
+		dev_err(&pon->pdev->dev,
+			"Unable to create debugfs directory\n");
+	} else {
+		ent = debugfs_create_file("uvlo_panic", 0644,
+				pon->debugfs, pon, &qpnp_pon_debugfs_uvlo_fops);
+		if (!ent)
+			dev_err(&pon->pdev->dev,
+				"Unable to create uvlo_panic debugfs file.\n");
+	}
+}
+
+static void qpnp_pon_debugfs_remove(struct platform_device *pdev)
+{
+	struct qpnp_pon *pon = dev_get_drvdata(&pdev->dev);
+
+	debugfs_remove_recursive(pon->debugfs);
+}
+
+#else
+
+static void qpnp_pon_debugfs_init(struct platform_device *pdev)
+{}
+
+static void qpnp_pon_debugfs_remove(struct platform_device *pdev)
+{}
+#endif
+
+static int read_gen2_pon_off_reason(struct qpnp_pon *pon, u16 *reason,
+					int *reason_index_offset)
+{
+	int rc;
+	int buf[2], reg;
+
+	rc = regmap_read(pon->regmap,
+			QPNP_PON_OFF_REASON(pon),
+			 &reg);
+	if (rc) {
+		dev_err(&pon->pdev->dev, "Unable to read PON_OFF_REASON reg rc:%d\n",
+			rc);
+		return rc;
+	}
+
+	if (reg & QPNP_GEN2_POFF_SEQ) {
+		rc = regmap_read(pon->regmap,
+				QPNP_POFF_REASON1(pon),
+				buf);
+		if (rc) {
+			dev_err(&pon->pdev->dev, "Unable to read POFF_REASON1 reg rc:%d\n",
+				rc);
+			return rc;
+		}
+		*reason = (u8)buf[0];
+		*reason_index_offset = 0;
+	} else if (reg & QPNP_GEN2_FAULT_SEQ) {
+		rc = regmap_bulk_read(pon->regmap,
+				QPNP_FAULT_REASON1(pon),
+				buf, 2);
+		if (rc) {
+			dev_err(&pon->pdev->dev, "Unable to read FAULT_REASON regs rc:%d\n",
+				rc);
+			return rc;
+		}
+		*reason = (u8)buf[0] | (u16)(buf[1] << 8);
+		*reason_index_offset = POFF_REASON_FAULT_OFFSET;
+	} else if (reg & QPNP_GEN2_S3_RESET_SEQ) {
+		rc = regmap_read(pon->regmap,
+				QPNP_S3_RESET_REASON(pon),
+				buf);
+		if (rc) {
+			dev_err(&pon->pdev->dev, "Unable to read S3_RESET_REASON reg rc:%d\n",
+				rc);
+			return rc;
+		}
+		*reason = (u8)buf[0];
+		*reason_index_offset = POFF_REASON_S3_RESET_OFFSET;
+	}
+
+	return 0;
+}
+
+static int qpnp_pon_probe(struct platform_device *pdev)
+{
+	struct qpnp_pon *pon;
+	unsigned int base;
+	struct device_node *node = NULL;
+	u32 delay = 0, s3_debounce = 0;
+	int rc, sys_reset, index;
+	int reason_index_offset = 0;
+	u8 buf[2];
+	uint pon_sts = 0;
+	u16 poff_sts = 0;
+	const char *s3_src;
+	u8 s3_src_reg;
+	unsigned long flags;
+	uint temp = 0;
+
+	pon = devm_kzalloc(&pdev->dev, sizeof(struct qpnp_pon), GFP_KERNEL);
+	if (!pon)
+		return -ENOMEM;
+
+	pon->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!pon->regmap) {
+		dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+		return -EINVAL;
+	}
+
+	sys_reset = of_property_read_bool(pdev->dev.of_node,
+						"qcom,system-reset");
+	if (sys_reset && sys_reset_dev) {
+		dev_err(&pdev->dev,
+			"qcom,system-reset property can only be specified for one device on the system\n");
+		return -EINVAL;
+	} else if (sys_reset) {
+		sys_reset_dev = pon;
+	}
+
+	pon->pdev = pdev;
+
+	rc = of_property_read_u32(pdev->dev.of_node, "reg", &base);
+	if (rc < 0) {
+		dev_err(&pdev->dev,
+			"Couldn't find reg in node = %s rc = %d\n",
+			pdev->dev.of_node->full_name, rc);
+		return rc;
+	}
+	pon->base = base;
+
+	/* get the total number of pon configurations */
+	for_each_available_child_of_node(pdev->dev.of_node, node) {
+		if (of_find_property(node, "regulator-name", NULL)) {
+			pon->num_pon_reg++;
+		} else if (of_find_property(node, "qcom,pon-type", NULL)) {
+			pon->num_pon_config++;
+		} else {
+			pr_err("Unknown sub-node\n");
+			return -EINVAL;
+		}
+	}
+
+	pr_debug("PON@SID %d: num_pon_config: %d num_pon_reg: %d\n",
+		to_spmi_device(pon->pdev->dev.parent)->usid,
+		pon->num_pon_config, pon->num_pon_reg);
+
+	rc = pon_regulator_init(pon);
+	if (rc) {
+		dev_err(&pdev->dev, "Error in pon_regulator_init rc: %d\n",
+			rc);
+		return rc;
+	}
+
+	if (!pon->num_pon_config)
+		/* No PON config., do not register the driver */
+		dev_info(&pdev->dev, "No PON config. specified\n");
+	else
+		pon->pon_cfg = devm_kzalloc(&pdev->dev,
+				sizeof(struct qpnp_pon_config) *
+				pon->num_pon_config, GFP_KERNEL);
+
+	/* Read PON_PERPH_SUBTYPE register to get PON type */
+	rc = regmap_read(pon->regmap,
+				QPNP_PON_PERPH_SUBTYPE(pon),
+				&temp);
+	if (rc) {
+		dev_err(&pon->pdev->dev,
+			"Unable to read PON_PERPH_SUBTYPE register rc: %d\n",
+			rc);
+		return rc;
+	}
+	pon->subtype = temp;
+
+	/* Check if it is rev B */
+	rc = regmap_read(pon->regmap,
+			QPNP_PON_REVISION2(pon), &temp);
+	if (rc) {
+		dev_err(&pon->pdev->dev,
+			"Unable to read addr=%x, rc(%d)\n",
+			QPNP_PON_REVISION2(pon), rc);
+		return rc;
+	}
+
+	pon->pon_ver = temp;
+	if (is_pon_gen1(pon)) {
+		if (pon->pon_ver == 0)
+			pon->pon_ver = QPNP_PON_GEN1_V1;
+		else
+			pon->pon_ver = QPNP_PON_GEN1_V2;
+	} else if (is_pon_gen2(pon)) {
+		pon->pon_ver = QPNP_PON_GEN2;
+	} else if (pon->subtype == PON_1REG) {
+		pon->pon_ver = QPNP_PON_GEN1_V2;
+	} else {
+		dev_err(&pon->pdev->dev,
+			"Invalid PON_PERPH_SUBTYPE value %x\n",
+			pon->subtype);
+		return -EINVAL;
+	}
+
+	pr_debug("%s: pon_subtype=%x, pon_version=%x\n", __func__,
+			pon->subtype, pon->pon_ver);
+
+	rc = qpnp_pon_store_and_clear_warm_reset(pon);
+	if (rc) {
+		dev_err(&pon->pdev->dev,
+			"Unable to store/clear WARM_RESET_REASONx registers rc: %d\n",
+			rc);
+		return rc;
+	}
+
+	/* PON reason */
+	rc = regmap_read(pon->regmap, QPNP_PON_REASON1(pon), &pon_sts);
+	if (rc) {
+		dev_err(&pon->pdev->dev,
+			"Unable to read PON_RESASON1 reg rc: %d\n",
+			rc);
+		return rc;
+	}
+
+	index = ffs(pon_sts) - 1;
+	cold_boot = !qpnp_pon_is_warm_reset();
+	if (index >= ARRAY_SIZE(qpnp_pon_reason) || index < 0) {
+		dev_info(&pon->pdev->dev,
+			"PMIC@SID%d Power-on reason: Unknown and '%s' boot\n",
+			to_spmi_device(pon->pdev->dev.parent)->usid,
+			 cold_boot ? "cold" : "warm");
+	} else {
+		pon->pon_trigger_reason = index;
+		dev_info(&pon->pdev->dev,
+			"PMIC@SID%d Power-on reason: %s and '%s' boot\n",
+			to_spmi_device(pon->pdev->dev.parent)->usid,
+			 qpnp_pon_reason[index],
+			cold_boot ? "cold" : "warm");
+	}
+
+	/* POFF reason */
+	if (!is_pon_gen1(pon) && pon->subtype != PON_1REG) {
+		rc = read_gen2_pon_off_reason(pon, &poff_sts,
+						&reason_index_offset);
+		if (rc)
+			return rc;
+	} else {
+		rc = regmap_bulk_read(pon->regmap, QPNP_POFF_REASON1(pon),
+			buf, 2);
+		if (rc) {
+			dev_err(&pon->pdev->dev, "Unable to read POFF_REASON regs rc:%d\n",
+				rc);
+			return rc;
+		}
+		poff_sts = buf[0] | (buf[1] << 8);
+	}
+	index = ffs(poff_sts) - 1 + reason_index_offset;
+	if (index >= ARRAY_SIZE(qpnp_poff_reason) || index < 0) {
+		dev_info(&pon->pdev->dev,
+				"PMIC@SID%d: Unknown power-off reason\n",
+				to_spmi_device(pon->pdev->dev.parent)->usid);
+	} else {
+		pon->pon_power_off_reason = index;
+		dev_info(&pon->pdev->dev,
+				"PMIC@SID%d: Power-off reason: %s\n",
+				to_spmi_device(pon->pdev->dev.parent)->usid,
+				qpnp_poff_reason[index]);
+	}
+
+	if (pon->pon_trigger_reason == PON_SMPL ||
+		pon->pon_power_off_reason == QPNP_POFF_REASON_UVLO) {
+		if (of_property_read_bool(pdev->dev.of_node,
+						"qcom,uvlo-panic"))
+			panic("An UVLO was occurred.");
+	}
+
+	/* program s3 debounce */
+	rc = of_property_read_u32(pon->pdev->dev.of_node,
+				"qcom,s3-debounce", &s3_debounce);
+	if (rc) {
+		if (rc != -EINVAL) {
+			dev_err(&pon->pdev->dev,
+				"Unable to read s3 timer rc:%d\n",
+				rc);
+			return rc;
+		}
+	} else {
+		if (s3_debounce > QPNP_PON_S3_TIMER_SECS_MAX) {
+			dev_info(&pon->pdev->dev,
+				"Exceeded S3 max value, set it to max\n");
+			s3_debounce = QPNP_PON_S3_TIMER_SECS_MAX;
+		}
+
+		/* 0 is a special value to indicate instant s3 reset */
+		if (s3_debounce != 0)
+			s3_debounce = ilog2(s3_debounce);
+
+		/* s3 debounce is SEC_ACCESS register */
+		rc = qpnp_pon_masked_write(pon, QPNP_PON_SEC_ACCESS(pon),
+					0xFF, QPNP_PON_SEC_UNLOCK);
+		if (rc) {
+			dev_err(&pdev->dev, "Unable to do SEC_ACCESS rc:%d\n",
+				rc);
+			return rc;
+		}
+
+		rc = qpnp_pon_masked_write(pon, QPNP_PON_S3_DBC_CTL(pon),
+				QPNP_PON_S3_DBC_DELAY_MASK, s3_debounce);
+		if (rc) {
+			dev_err(&pdev->dev,
+				"Unable to set S3 debounce rc:%d\n",
+				rc);
+			return rc;
+		}
+	}
+
+	/* program s3 source */
+	s3_src = "kpdpwr-and-resin";
+	rc = of_property_read_string(pon->pdev->dev.of_node,
+				"qcom,s3-src", &s3_src);
+	if (rc && rc != -EINVAL) {
+		dev_err(&pon->pdev->dev, "Unable to read s3 timer rc: %d\n",
+			rc);
+		return rc;
+	}
+
+	if (!strcmp(s3_src, "kpdpwr"))
+		s3_src_reg = QPNP_PON_S3_SRC_KPDPWR;
+	else if (!strcmp(s3_src, "resin"))
+		s3_src_reg = QPNP_PON_S3_SRC_RESIN;
+	else if (!strcmp(s3_src, "kpdpwr-or-resin"))
+		s3_src_reg = QPNP_PON_S3_SRC_KPDPWR_OR_RESIN;
+	else /* default combination */
+		s3_src_reg = QPNP_PON_S3_SRC_KPDPWR_AND_RESIN;
+
+	/*
+	 * S3 source is a write once register. If the register has
+	 * been configured by bootloader then this operation will
+	 * not be effective.
+	 */
+	rc = qpnp_pon_masked_write(pon, QPNP_PON_S3_SRC(pon),
+			QPNP_PON_S3_SRC_MASK, s3_src_reg);
+	if (rc) {
+		dev_err(&pdev->dev, "Unable to program s3 source rc: %d\n",
+			rc);
+		return rc;
+	}
+
+	dev_set_drvdata(&pdev->dev, pon);
+
+	INIT_DELAYED_WORK(&pon->bark_work, bark_work_func);
+
+	/* register the PON configurations */
+	rc = qpnp_pon_config_init(pon);
+	if (rc) {
+		dev_err(&pdev->dev,
+			"Unable to initialize PON configurations rc: %d\n", rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(pon->pdev->dev.of_node,
+				"qcom,pon-dbc-delay", &delay);
+	if (rc) {
+		if (rc != -EINVAL) {
+			dev_err(&pdev->dev,
+				"Unable to read debounce delay rc: %d\n", rc);
+			return rc;
+		}
+	} else {
+		rc = qpnp_pon_set_dbc(pon, delay);
+	}
+
+	rc = of_property_read_u32(pon->pdev->dev.of_node,
+				"qcom,warm-reset-poweroff-type",
+				&pon->warm_reset_poff_type);
+	if (rc) {
+		if (rc != -EINVAL) {
+			dev_err(&pdev->dev, "Unable to read warm reset poweroff type rc: %d\n",
+				rc);
+			return rc;
+		}
+		pon->warm_reset_poff_type = -EINVAL;
+	} else if (pon->warm_reset_poff_type <= PON_POWER_OFF_RESERVED ||
+			pon->warm_reset_poff_type >= PON_POWER_OFF_MAX_TYPE) {
+		dev_err(&pdev->dev, "Invalid warm-reset-poweroff-type\n");
+		pon->warm_reset_poff_type = -EINVAL;
+	}
+
+	rc = of_property_read_u32(pon->pdev->dev.of_node,
+				"qcom,hard-reset-poweroff-type",
+				&pon->hard_reset_poff_type);
+	if (rc) {
+		if (rc != -EINVAL) {
+			dev_err(&pdev->dev, "Unable to read hard reset poweroff type rc: %d\n",
+				rc);
+			return rc;
+		}
+		pon->hard_reset_poff_type = -EINVAL;
+	} else if (pon->hard_reset_poff_type <= PON_POWER_OFF_RESERVED ||
+			pon->hard_reset_poff_type >= PON_POWER_OFF_MAX_TYPE) {
+		dev_err(&pdev->dev, "Invalid hard-reset-poweroff-type\n");
+		pon->hard_reset_poff_type = -EINVAL;
+	}
+
+	rc = of_property_read_u32(pon->pdev->dev.of_node,
+				"qcom,shutdown-poweroff-type",
+				&pon->shutdown_poff_type);
+	if (rc) {
+		if (rc != -EINVAL) {
+			dev_err(&pdev->dev, "Unable to read shutdown poweroff type rc: %d\n",
+				rc);
+			return rc;
+		}
+		pon->shutdown_poff_type = -EINVAL;
+	} else if (pon->shutdown_poff_type <= PON_POWER_OFF_RESERVED ||
+			pon->shutdown_poff_type >= PON_POWER_OFF_MAX_TYPE) {
+		dev_err(&pdev->dev, "Invalid shutdown-poweroff-type\n");
+		pon->shutdown_poff_type = -EINVAL;
+	}
+
+	rc = device_create_file(&pdev->dev, &dev_attr_debounce_us);
+	if (rc) {
+		dev_err(&pdev->dev, "sys file creation failed rc: %d\n", rc);
+		return rc;
+	}
+
+	if (of_property_read_bool(pdev->dev.of_node,
+					"qcom,secondary-pon-reset")) {
+		if (sys_reset) {
+			dev_err(&pdev->dev,
+				"qcom,system-reset property shouldn't be used along with qcom,secondary-pon-reset property\n");
+			return -EINVAL;
+		}
+		spin_lock_irqsave(&spon_list_slock, flags);
+		list_add(&pon->list, &spon_dev_list);
+		spin_unlock_irqrestore(&spon_list_slock, flags);
+		pon->is_spon = true;
+	} else {
+		boot_reason = ffs(pon_sts);
+	}
+
+	/* config whether store the hard reset reason */
+	pon->store_hard_reset_reason = of_property_read_bool(pdev->dev.of_node,
+					"qcom,store-hard-reset-reason");
+
+	qpnp_pon_debugfs_init(pdev);
+	return 0;
+}
+
+static int qpnp_pon_remove(struct platform_device *pdev)
+{
+	struct qpnp_pon *pon = dev_get_drvdata(&pdev->dev);
+	unsigned long flags;
+
+	device_remove_file(&pdev->dev, &dev_attr_debounce_us);
+
+	cancel_delayed_work_sync(&pon->bark_work);
+
+	if (pon->pon_input)
+		input_unregister_device(pon->pon_input);
+	qpnp_pon_debugfs_remove(pdev);
+	if (pon->is_spon) {
+		spin_lock_irqsave(&spon_list_slock, flags);
+		list_del(&pon->list);
+		spin_unlock_irqrestore(&spon_list_slock, flags);
+	}
+	return 0;
+}
+
+static const struct of_device_id spmi_match_table[] = {
+	{ .compatible = "qcom,qpnp-power-on", },
+	{}
+};
+
+static struct platform_driver qpnp_pon_driver = {
+	.driver		= {
+		.name		= "qcom,qpnp-power-on",
+		.of_match_table	= spmi_match_table,
+	},
+	.probe		= qpnp_pon_probe,
+	.remove		= qpnp_pon_remove,
+};
+
+static int __init qpnp_pon_init(void)
+{
+	return platform_driver_register(&qpnp_pon_driver);
+}
+subsys_initcall(qpnp_pon_init);
+
+static void __exit qpnp_pon_exit(void)
+{
+	return platform_driver_unregister(&qpnp_pon_driver);
+}
+module_exit(qpnp_pon_exit);
+
+MODULE_DESCRIPTION("QPNP PMIC POWER-ON driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c
index 9e3c927..bae9ef2 100644
--- a/drivers/power/reset/msm-poweroff.c
+++ b/drivers/power/reset/msm-poweroff.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -23,7 +23,7 @@
 #include <linux/reboot.h>
 #include <linux/pm.h>
 #include <linux/delay.h>
-#include <linux/qpnp/power-on.h>
+#include <linux/input/qpnp-power-on.h>
 #include <linux/of_address.h>
 
 #include <asm/cacheflush.h>
diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c
index fdb824f..7a22d8c 100644
--- a/drivers/power/supply/power_supply_sysfs.c
+++ b/drivers/power/supply/power_supply_sysfs.c
@@ -205,6 +205,7 @@ static struct device_attribute power_supply_attrs[] = {
 	POWER_SUPPLY_ATTR(usb_hc),
 	POWER_SUPPLY_ATTR(usb_otg),
 	POWER_SUPPLY_ATTR(charge_enabled),
+	POWER_SUPPLY_ATTR(set_ship_mode),
 	/* Local extensions of type int64_t */
 	POWER_SUPPLY_ATTR(charge_counter_ext),
 	/* Properties of type `const char *' */
diff --git a/drivers/soc/qcom/llcc-msmskunk.c b/drivers/soc/qcom/llcc-msmskunk.c
index 65c79f7..41f55eb 100644
--- a/drivers/soc/qcom/llcc-msmskunk.c
+++ b/drivers/soc/qcom/llcc-msmskunk.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -57,24 +57,28 @@
 	}
 
 static struct llcc_slice_config msmskunk_data[] =  {
-	SCT_ENTRY("cpuss",       1, 1, 3072, 0, 0, 0xFFF, 0x0, 0, 0, 0, 1, 1),
+	SCT_ENTRY("cpuss",       1, 1, 3072, 1, 0, 0xFFF, 0x0, 0, 0, 0, 1, 1),
 	SCT_ENTRY("vidsc0",      2, 2, 512, 2, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0),
 	SCT_ENTRY("vidsc1",      3, 3, 512, 2, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0),
-	SCT_ENTRY("rotator",     4, 4, 800, 2, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0),
-	SCT_ENTRY("voice",       5, 5, 3072, 1, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0),
-	SCT_ENTRY("audio",       6, 6, 3072, 0, 0, 0xFFF, 0x0, 0, 0, 0, 1, 0),
-	SCT_ENTRY("modemhp",     7, 7, 1024, 2, 0, 0xFFF, 0x0, 0, 0, 0, 1, 0),
-	SCT_ENTRY("modem",       8, 8, 1024, 0, 1, 0xF,  0x0, 0, 0, 0, 1, 0),
-	SCT_ENTRY("modemhw",     9, 9, 3072, 0, 0, 0xFFF, 0x0, 0, 0, 0, 1, 0),
-	SCT_ENTRY("compute",     10, 10, 3072, 0, 0, 0xFFF, 0x0, 0, 0, 0, 1, 0),
-	SCT_ENTRY("gpuhtw",      11, 11, 3072, 0, 0, 0xFFF, 0x0, 0, 0, 0, 1, 0),
-	SCT_ENTRY("gpu",         12, 12, 3072, 0, 0, 0xFFF, 0x0, 0, 0, 0, 1, 0),
-	SCT_ENTRY("mmuhwt",      13, 13, 3072, 0, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1),
-	SCT_ENTRY("sensor",      14, 14, 3072, 0, 0, 0xFFF, 0x0, 0, 0, 0, 1, 0),
-	SCT_ENTRY("compute_dma", 15, 15, 3072, 0, 0, 0xFFF, 0x0, 0, 0, 0, 1, 0),
-	SCT_ENTRY("display",     16, 16, 3072, 0, 0, 0xFFF, 0x0, 0, 0, 0, 1, 0),
-	SCT_ENTRY("videofw",     17, 17, 3072, 0, 0, 0xFFF, 0x0, 0, 0, 0, 1, 0),
-	SCT_ENTRY("camerafw",    18, 18, 224, 0, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0),
+	SCT_ENTRY("rotator",     4, 4, 563, 2, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0),
+	SCT_ENTRY("voice",       5, 5, 3072, 1, 0, 0xFFF, 0x0, 0, 0, 0, 1, 0),
+	SCT_ENTRY("audio",       6, 6, 3072, 1, 0, 0xFFF, 0x0, 0, 0, 0, 1, 0),
+	SCT_ENTRY("modemhp_grow", 7, 7, 1024, 2, 0, 0xFFF, 0x0, 0, 0, 0, 1, 0),
+	SCT_ENTRY("modem",       8, 8, 3072, 1, 0, 0xFFF, 0x0, 0, 0, 0, 1, 0),
+	SCT_ENTRY("modemhw",     9, 9, 3072, 1, 0, 0xFFF, 0x0, 0, 0, 0, 1, 0),
+	SCT_ENTRY("compute",     10, 10, 3072, 1, 0, 0xFFF, 0x0, 0, 0, 0, 1, 0),
+	SCT_ENTRY("gpuhtw",      11, 11, 3072, 1, 0, 0xFFF, 0x0, 0, 0, 0, 1, 0),
+	SCT_ENTRY("gpu",         12, 12, 3072, 1, 0, 0xFFF, 0x0, 0, 0, 0, 1, 0),
+	SCT_ENTRY("mmuhwt",      13, 13, 3072, 1, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1),
+	SCT_ENTRY("sensor",      14, 14, 3072, 1, 0, 0xFFF, 0x0, 0, 0, 0, 1, 0),
+	SCT_ENTRY("compute_dma", 15, 15, 3072, 1, 0, 0xFFF, 0x0, 0, 0, 0, 1, 0),
+	SCT_ENTRY("display",     16, 16, 3072, 1, 0, 0xFFF, 0x0, 0, 0, 0, 1, 0),
+	SCT_ENTRY("videofw",     17, 17, 3072, 1, 0, 0xFFF, 0x0, 0, 0, 0, 1, 0),
+	SCT_ENTRY("camerafw",    18, 18, 256, 1, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0),
+	SCT_ENTRY("mss_tcm",     19, 19, 1024, 0, 0, 0x0,  0xf, 1, 0, 1, 1, 0),
+	SCT_ENTRY("modemhp_fix", 20, 20, 1024, 2, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0),
+	SCT_ENTRY("modem_paging", 21, 21, 1024, 0, 1, 0xF,  0x0, 0, 0, 0, 1, 0),
+	SCT_ENTRY("audiohw",     22, 22, 3072, 1, 0, 0xFFF, 0x0, 0, 0, 0, 1, 0),
 };
 
 static int msmskunk_qcom_llcc_probe(struct platform_device *pdev)
diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c
index 1e593e0..0dd8b26 100644
--- a/drivers/soc/qcom/peripheral-loader.c
+++ b/drivers/soc/qcom/peripheral-loader.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -627,6 +627,7 @@ static int pil_load_seg(struct pil_desc *desc, struct pil_seg *seg)
 	char fw_name[30];
 	int num = seg->num;
 	const struct firmware *fw = NULL;
+	void __iomem *firmware_buf;
 	struct pil_map_fw_info map_fw_info = {
 		.attrs = desc->attrs,
 		.region = desc->priv->region,
@@ -638,20 +639,28 @@ static int pil_load_seg(struct pil_desc *desc, struct pil_seg *seg)
 	if (seg->filesz) {
 		snprintf(fw_name, ARRAY_SIZE(fw_name), "%s.b%02d",
 				desc->fw_name, num);
+		firmware_buf = desc->map_fw_mem(seg->paddr, seg->filesz,
+						map_data);
+		if (!firmware_buf) {
+			pil_err(desc, "Failed to map memory for firmware buffer\n");
+			return -ENOMEM;
+		}
+
 		ret = request_firmware_into_buf(&fw, fw_name, desc->dev,
-						map_data, seg->filesz);
-		if (ret < 0) {
+						firmware_buf, seg->filesz);
+		desc->unmap_fw_mem(firmware_buf, seg->filesz, map_data);
+
+		if (ret) {
 			pil_err(desc, "Failed to locate blob %s or blob is too big(rc:%d)\n",
 				fw_name, ret);
 			return ret;
 		}
 
-		if (ret != seg->filesz) {
+		if (fw->size != seg->filesz) {
 			pil_err(desc, "Blob size %u doesn't match %lu\n",
 					ret, seg->filesz);
 			return -EPERM;
 		}
-		ret = 0;
 	}
 
 	/* Zero out trailing memory */
diff --git a/drivers/soc/qcom/subsys-pil-tz.c b/drivers/soc/qcom/subsys-pil-tz.c
index a7d5d37..a46dbbd 100644
--- a/drivers/soc/qcom/subsys-pil-tz.c
+++ b/drivers/soc/qcom/subsys-pil-tz.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -1039,7 +1039,7 @@ static int pil_tz_driver_probe(struct platform_device *pdev)
 	if (!d->subsys_desc.no_auth) {
 		rc = piltz_resc_init(pdev, d);
 		if (rc)
-			return -ENOENT;
+			return rc;
 
 		rc = of_property_read_u32(pdev->dev.of_node, "qcom,pas-id",
 								&d->pas_id);
diff --git a/drivers/soc/qcom/subsystem_restart.c b/drivers/soc/qcom/subsystem_restart.c
index c6e288e..cb91789 100644
--- a/drivers/soc/qcom/subsystem_restart.c
+++ b/drivers/soc/qcom/subsystem_restart.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -1697,7 +1697,6 @@ struct subsys_device *subsys_register(struct subsys_desc *desc)
 	if (ret) {
 		subsys_debugfs_remove(subsys);
 		put_device(&subsys->dev);
-		kfree(subsys);
 		return ERR_PTR(ret);
 	}
 
@@ -1759,7 +1758,6 @@ struct subsys_device *subsys_register(struct subsys_desc *desc)
 err_register:
 	subsys_debugfs_remove(subsys);
 	device_unregister(&subsys->dev);
-	kfree(subsys);
 	return ERR_PTR(ret);
 }
 EXPORT_SYMBOL(subsys_register);
diff --git a/include/dt-bindings/clock/qcom,cpucc-skunk.h b/include/dt-bindings/clock/qcom,cpucc-skunk.h
new file mode 100644
index 0000000..2332969
--- /dev/null
+++ b/include/dt-bindings/clock/qcom,cpucc-skunk.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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.
+ */
+
+#ifndef _DT_BINDINGS_CLK_MSM_CPU_CC_SKUNK_H
+#define _DT_BINDINGS_CLK_MSM_CPU_CC_SKUNK_H
+
+#define L3_CLUSTER0_VOTE_CLK					0
+#define L3_CLUSTER1_VOTE_CLK					1
+#define L3_CLK							2
+#define CPU0_PWRCL_CLK						3
+#define CPU1_PWRCL_CLK						4
+#define CPU2_PWRCL_CLK						5
+#define CPU3_PWRCL_CLK						6
+#define PWRCL_CLK						7
+#define CPU4_PERFCL_CLK						8
+#define CPU5_PERFCL_CLK						9
+#define CPU6_PERFCL_CLK						10
+#define CPU7_PERFCL_CLK						11
+#define PERFCL_CLK						12
+
+#endif
diff --git a/include/linux/input/qpnp-power-on.h b/include/linux/input/qpnp-power-on.h
new file mode 100644
index 0000000..a2624ab
--- /dev/null
+++ b/include/linux/input/qpnp-power-on.h
@@ -0,0 +1,96 @@
+/* Copyright (c) 2012-2015, 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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.
+ */
+
+#ifndef QPNP_PON_H
+#define QPNP_PON_H
+
+#include <linux/errno.h>
+
+/**
+ * enum pon_trigger_source: List of PON trigger sources
+ * %PON_SMPL:		PON triggered by SMPL - Sudden Momentary Power Loss
+ * %PON_RTC:		PON triggered by RTC alarm
+ * %PON_DC_CHG:		PON triggered by insertion of DC charger
+ * %PON_USB_CHG:	PON triggered by insertion of USB
+ * %PON_PON1:		PON triggered by other PMIC (multi-PMIC option)
+ * %PON_CBLPWR_N:	PON triggered by power-cable insertion
+ * %PON_KPDPWR_N:	PON triggered by long press of the power-key
+ */
+enum pon_trigger_source {
+	PON_SMPL = 1,
+	PON_RTC,
+	PON_DC_CHG,
+	PON_USB_CHG,
+	PON_PON1,
+	PON_CBLPWR_N,
+	PON_KPDPWR_N,
+};
+
+/**
+ * enum pon_power_off_type: Possible power off actions to perform
+ * %PON_POWER_OFF_RESERVED:          Reserved, not used
+ * %PON_POWER_OFF_WARM_RESET:        Reset the MSM but not all PMIC peripherals
+ * %PON_POWER_OFF_SHUTDOWN:          Shutdown the MSM and PMIC completely
+ * %PON_POWER_OFF_HARD_RESET:        Reset the MSM and all PMIC peripherals
+ */
+enum pon_power_off_type {
+	PON_POWER_OFF_RESERVED		= 0x00,
+	PON_POWER_OFF_WARM_RESET	= 0x01,
+	PON_POWER_OFF_SHUTDOWN		= 0x04,
+	PON_POWER_OFF_HARD_RESET	= 0x07,
+	PON_POWER_OFF_MAX_TYPE		= 0x10,
+};
+
+enum pon_restart_reason {
+	PON_RESTART_REASON_UNKNOWN		= 0x00,
+	PON_RESTART_REASON_RECOVERY		= 0x01,
+	PON_RESTART_REASON_BOOTLOADER		= 0x02,
+	PON_RESTART_REASON_RTC			= 0x03,
+	PON_RESTART_REASON_DMVERITY_CORRUPTED	= 0x04,
+	PON_RESTART_REASON_DMVERITY_ENFORCE	= 0x05,
+	PON_RESTART_REASON_KEYS_CLEAR		= 0x06,
+};
+
+#ifdef CONFIG_INPUT_QPNP_POWER_ON
+int qpnp_pon_system_pwr_off(enum pon_power_off_type type);
+int qpnp_pon_is_warm_reset(void);
+int qpnp_pon_trigger_config(enum pon_trigger_source pon_src, bool enable);
+int qpnp_pon_wd_config(bool enable);
+int qpnp_pon_set_restart_reason(enum pon_restart_reason reason);
+bool qpnp_pon_check_hard_reset_stored(void);
+
+#else
+static int qpnp_pon_system_pwr_off(enum pon_power_off_type type)
+{
+	return -ENODEV;
+}
+static inline int qpnp_pon_is_warm_reset(void) { return -ENODEV; }
+static inline int qpnp_pon_trigger_config(enum pon_trigger_source pon_src,
+							bool enable)
+{
+	return -ENODEV;
+}
+int qpnp_pon_wd_config(bool enable)
+{
+	return -ENODEV;
+}
+static inline int qpnp_pon_set_restart_reason(enum pon_restart_reason reason)
+{
+	return -ENODEV;
+}
+static inline bool qpnp_pon_check_hard_reset_stored(void)
+{
+	return false;
+}
+#endif
+
+#endif
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index ecfb4cac..c30fd4b 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -153,6 +153,7 @@ enum power_supply_property {
 	POWER_SUPPLY_PROP_USB_HC,
 	POWER_SUPPLY_PROP_USB_OTG,
 	POWER_SUPPLY_PROP_CHARGE_ENABLED,
+	POWER_SUPPLY_PROP_SET_SHIP_MODE,
 	/* Local extensions of type int64_t */
 	POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT,
 	/* Properties of type `const char *' */
diff --git a/include/uapi/linux/sysctl.h b/include/uapi/linux/sysctl.h
index d2b1215..e854785 100644
--- a/include/uapi/linux/sysctl.h
+++ b/include/uapi/linux/sysctl.h
@@ -152,6 +152,8 @@ enum
 	KERN_NMI_WATCHDOG=75, /* int: enable/disable nmi watchdog */
 	KERN_PANIC_ON_NMI=76, /* int: whether we will panic on an unrecovered */
 	KERN_PANIC_ON_WARN=77, /* int: call panic() in WARN() functions */
+	KERN_BOOT_REASON=78, /* int: identify reason system was booted */
+	KERN_COLD_BOOT=79, /* int: identify if system cold booted */
 };
 
 
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index fe65371..337cb1e 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -1398,6 +1398,23 @@ static struct ctl_table kern_table[] = {
 		.extra2		= &one,
 	},
 #endif
+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
+	{
+		.procname	= "boot_reason",
+		.data		= &boot_reason,
+		.maxlen		= sizeof(int),
+		.mode		= 0444,
+		.proc_handler	= proc_dointvec,
+	},
+
+	{
+		.procname	= "cold_boot",
+		.data		= &cold_boot,
+		.maxlen		= sizeof(int),
+		.mode		= 0444,
+		.proc_handler	= proc_dointvec,
+	},
+#endif
 	{ }
 };
 
diff --git a/kernel/sysctl_binary.c b/kernel/sysctl_binary.c
index 6eb99c1..33fba7f 100644
--- a/kernel/sysctl_binary.c
+++ b/kernel/sysctl_binary.c
@@ -139,6 +139,8 @@ static const struct bin_table bin_kern_table[] = {
 	{ CTL_INT,	KERN_MAX_LOCK_DEPTH,		"max_lock_depth" },
 	{ CTL_INT,	KERN_PANIC_ON_NMI,		"panic_on_unrecovered_nmi" },
 	{ CTL_INT,	KERN_PANIC_ON_WARN,		"panic_on_warn" },
+	{ CTL_INT,	KERN_BOOT_REASON,		"boot_reason" },
+	{ CTL_INT,	KERN_COLD_BOOT,			"cold_boot" },
 	{}
 };
 
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 21c3ef0..ab07789 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -180,7 +180,11 @@ int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir,
 
 		dev_dbg(be->dev, "ASoC: BE %s event %d dir %d\n",
 				be->dai_link->name, event, dir);
-
+		if ((event == SND_SOC_DAPM_STREAM_STOP) &&
+		    (be->dpcm[dir].users >= 1)) {
+			pr_debug("%s Don't close BE\n", __func__);
+			continue;
+		}
 		snd_soc_dapm_stream_event(be, dir, event);
 	}