diff --git a/drivers/platform/msm/qpnp-power-on.c b/drivers/platform/msm/qpnp-power-on.c
index 0d9c9233918d4e151dfcf3daf5ce7bb688de9045..03672e5ebf1cd4246208b907eadf9b19ca33b0b5 100644
--- a/drivers/platform/msm/qpnp-power-on.c
+++ b/drivers/platform/msm/qpnp-power-on.c
@@ -23,6 +23,10 @@
 #include <linux/input.h>
 #include <linux/log2.h>
 #include <linux/qpnp/power-on.h>
+#include <linux/timer.h>
+
+#include "../../staging/android/timed_output.h"
+#include <linux/platform_data/msm_pwm_vibrator.h>
 
 /* Common PNP defines */
 #define QPNP_PON_REVISION2(base)		(base + 0x01)
@@ -102,6 +106,11 @@
 
 #define QPNP_PON_BUFFER_SIZE			9
 
+#define QPNP_LONGKEY_WARN_TIME_MAX		5000 /* 5 sec */
+#define QPNP_VIBE_TIME				2000 /* 2 sec */
+#define QPNP_VIBE_INIT_DELAY			msecs_to_jiffies(2000)
+#define QPNP_VIBE_INIT_RETRY			5
+
 enum pon_type {
 	PON_KPDPWR,
 	PON_RESIN,
@@ -133,6 +142,13 @@ struct qpnp_pon {
 	u16 base;
 	struct delayed_work bark_work;
 	u32 dbc;
+	struct delayed_work timed_work;
+	struct timed_output_dev *timed_dev;
+	struct hrtimer timed_timer;
+	int longkey_warn_time;
+	bool timed_inited;
+	bool vibed;
+	spinlock_t lock;
 };
 
 static struct qpnp_pon *sys_reset_dev;
@@ -450,6 +466,96 @@ qpnp_get_cfg(struct qpnp_pon *pon, u32 pon_type)
 	return NULL;
 }
 
+static void qpnp_pon_enable_vibrator(struct qpnp_pon *pon, int timeout)
+{
+	struct timed_output_dev *dev = pon->timed_dev;
+	unsigned long flags;
+
+	if (dev && dev->enable) {
+		spin_lock_irqsave(&pon->lock, flags);
+		if (timeout || pon->vibed)
+			dev->enable(dev, timeout);
+		pon->vibed = !!timeout;
+		spin_unlock_irqrestore(&pon->lock, flags);
+	}
+}
+
+static enum hrtimer_restart qpnp_pon_vibe_timer_func(struct hrtimer *timer)
+{
+	struct qpnp_pon *pon =
+		container_of(timer, struct qpnp_pon, timed_timer);
+
+	qpnp_pon_enable_vibrator(pon, QPNP_VIBE_TIME);
+
+	return HRTIMER_NORESTART;
+}
+
+static void timed_work_func(struct work_struct *work)
+{
+	struct qpnp_pon *pon =
+		container_of(work, struct qpnp_pon, timed_work.work);
+	struct spmi_device *spmi;
+	struct device_node *dev_node;
+	struct device_node *node;
+	struct platform_device *pdev;
+	struct timed_vibrator_data *vib_data;
+	int rc;
+	static int retry = 0;
+
+	if (!pon) {
+		pr_warn("%s: no device\n", __func__);
+		return;
+	}
+
+	spmi = pon->spmi;
+	dev_node = spmi->dev.of_node;
+
+	node = of_parse_phandle(dev_node, "qcom,external-vibrator", 0);
+	if (!node) {
+		dev_warn(&spmi->dev,
+			"Can't find qcom,external-vibrator phandle\n");
+		return;
+	}
+
+	pdev = of_find_device_by_node(node);
+	if (!pdev) {
+		dev_warn(&spmi->dev,
+			"Can't find the device by node\n");
+		return;
+	}
+
+	vib_data = platform_get_drvdata(pdev);
+	if (!vib_data) {
+		dev_warn(&spmi->dev,
+			"Can't find the vibrator data by pdev\n");
+
+		/* re-arm the work */
+		if (retry < QPNP_VIBE_INIT_RETRY) {
+			retry++;
+			dev_warn(&spmi->dev,
+				"Re-armed timed init work\n");
+			schedule_delayed_work(&pon->timed_work,
+					QPNP_VIBE_INIT_DELAY);
+		}
+		return;
+	}
+
+	rc = of_property_read_u32(dev_node, "qcom,longkey-warn-time",
+					&pon->longkey_warn_time);
+	if (rc && rc != -EINVAL) {
+		dev_warn(&spmi->dev,
+			"Unable to read 'qcom,longkey-warn-time'\n");
+		pon->longkey_warn_time = QPNP_LONGKEY_WARN_TIME_MAX;
+	}
+
+	pon->timed_dev = &vib_data->dev;
+
+	hrtimer_init(&pon->timed_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	pon->timed_timer.function = qpnp_pon_vibe_timer_func;
+
+	pon->timed_inited = true;
+}
+
 static int
 qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type)
 {
@@ -508,6 +614,20 @@ qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type)
 
 	cfg->old_state = !!key_status;
 
+	if (pon->timed_inited) {
+		if (key_status) {
+			hrtimer_start(&pon->timed_timer,
+				ns_to_ktime((u64)pon->longkey_warn_time *
+					     NSEC_PER_MSEC),
+				HRTIMER_MODE_REL);
+		} else {
+			if (hrtimer_active(&pon->timed_timer))
+				hrtimer_try_to_cancel(&pon->timed_timer);
+
+			qpnp_pon_enable_vibrator(pon, 0);
+		}
+	}
+
 	return 0;
 }
 
@@ -1355,6 +1475,9 @@ static int qpnp_pon_probe(struct spmi_device *spmi)
 	dev_set_drvdata(&spmi->dev, pon);
 
 	INIT_DELAYED_WORK(&pon->bark_work, bark_work_func);
+	INIT_DELAYED_WORK(&pon->timed_work, timed_work_func);
+
+	spin_lock_init(&pon->lock);
 
 	/* register the PON configurations */
 	rc = qpnp_pon_config_init(pon);
@@ -1383,6 +1506,8 @@ static int qpnp_pon_probe(struct spmi_device *spmi)
 		return rc;
 	}
 
+	schedule_delayed_work(&pon->timed_work, 0);
+
 	return rc;
 }