msm: qpnp-power-on: Add reset configuration support

The PON module supports various reset sources (physical pins)
such as KPDPWR, RESIN, RESIN+KPDPWR. These pins can be configured
for different reset types (such as warm, soft, hard) and can also
be used as push buttons (keys).

In the push-button configuration these pins act as gpios, only
reporting the state on the line (high/low) and no additional
pon based configuration being done in the hardware or software.
They can be used for generic buttons (such as volume up/down,
directional keys)

Change-Id: Icde78b49b1037c1b5a13d7e90f772f72ac822b2a
Signed-off-by: Anirudh Ghayal <aghayal@codeaurora.org>
diff --git a/drivers/platform/msm/qpnp-power-on.c b/drivers/platform/msm/qpnp-power-on.c
index d8bb884..aeaca85 100644
--- a/drivers/platform/msm/qpnp-power-on.c
+++ b/drivers/platform/msm/qpnp-power-on.c
@@ -16,141 +16,545 @@
 #include <linux/errno.h>
 #include <linux/slab.h>
 #include <linux/spmi.h>
+#include <linux/delay.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/interrupt.h>
 #include <linux/input.h>
+#include <linux/log2.h>
 
+/* PON common register addresses */
 #define QPNP_PON_RT_STS(base)		(base + 0x10)
 #define QPNP_PON_PULL_CTL(base)		(base + 0x70)
 #define QPNP_PON_DBC_CTL(base)		(base + 0x71)
 
-#define QPNP_PON_CNTL_PULL_UP		BIT(1)
-#define QPNP_PON_CNTL_TRIG_DELAY_MASK	(0x7)
+/* PON/RESET sources register addresses */
+#define QPNP_PON_KPDPWR_S1_TIMER(base)	(base + 0x40)
+#define QPNP_PON_KPDPWR_S2_TIMER(base)	(base + 0x41)
+#define QPNP_PON_KPDPWR_S2_CNTL(base)	(base + 0x42)
+#define QPNP_PON_RESIN_S1_TIMER(base)	(base + 0x44)
+#define QPNP_PON_RESIN_S2_TIMER(base)	(base + 0x45)
+#define QPNP_PON_RESIN_S2_CNTL(base)	(base + 0x46)
+
+#define QPNP_PON_RESIN_PULL_UP		BIT(0)
+#define QPNP_PON_KPDPWR_PULL_UP		BIT(1)
+#define QPNP_PON_S2_CNTL_EN		BIT(7)
+#define QPNP_PON_S2_RESET_ENABLE	BIT(7)
+
+#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		(0x7)
 #define QPNP_PON_KPDPWR_N_SET		BIT(0)
+#define QPNP_PON_RESIN_N_SET		BIT(1)
+
+/* Ranges */
+#define QPNP_PON_S1_TIMER_MAX		10256
+#define QPNP_PON_S2_TIMER_MAX		2000
+#define QPNP_PON_RESET_TYPE_MAX		0xF
+#define PON_S1_COUNT_MAX		0xF
+
+enum pon_type {
+	PON_KPDPWR,
+	PON_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;
+};
 
 struct qpnp_pon {
 	struct spmi_device *spmi;
 	struct input_dev *pon_input;
-	u32 key_status_irq;
+	struct qpnp_pon_config *pon_cfg;
+	int num_pon_config;
 	u16 base;
 };
 
-static irqreturn_t qpnp_pon_key_irq(int irq, void *_pon)
-{
-	u8 pon_rt_sts;
-	int rc;
-	struct qpnp_pon *pon = _pon;
+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 int
+qpnp_pon_masked_write(struct qpnp_pon *pon, u16 addr, u8 mask, u8 val)
+{
+	int rc;
+	u8 reg;
+
+	rc = spmi_ext_register_readl(pon->spmi->ctrl, pon->spmi->sid,
+							addr, &reg, 1);
+	if (rc) {
+		dev_err(&pon->spmi->dev,
+			"Unable to read from addr=%x, rc(%d)\n", addr, rc);
+		return rc;
+	}
+
+	reg &= ~mask;
+	reg |= val & mask;
+	rc = spmi_ext_register_writel(pon->spmi->ctrl, pon->spmi->sid,
+							addr, &reg, 1);
+	if (rc)
+		dev_err(&pon->spmi->dev,
+			"Unable to write to addr=%x, rc(%d)\n", addr, rc);
+	return rc;
+}
+
+static int
+qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type)
+{
+	int i, rc;
+	struct qpnp_pon_config *cfg = NULL;
+	u8 pon_rt_sts = 0, pon_rt_bit = 0;
+
+	for (i = 0; i < pon->num_pon_config; i++) {
+		/* get the configuration infor for that pon type */
+		if (pon_type == pon->pon_cfg[i].pon_type) {
+			cfg = &pon->pon_cfg[i];
+			break;
+		}
+	}
+
+	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 = spmi_ext_register_readl(pon->spmi->ctrl, pon->spmi->sid,
 				QPNP_PON_RT_STS(pon->base), &pon_rt_sts, 1);
 	if (rc) {
 		dev_err(&pon->spmi->dev, "Unable to read PON RT status\n");
-		return IRQ_HANDLED;
+		return rc;
 	}
 
-	input_report_key(pon->pon_input, KEY_POWER,
-				!(pon_rt_sts & QPNP_PON_KPDPWR_N_SET));
+	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;
+	default:
+		return -EINVAL;
+	}
+
+	input_report_key(pon->pon_input, cfg->key_code,
+					(pon_rt_sts & pon_rt_bit));
 	input_sync(pon->pon_input);
 
+	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->spmi->dev, "Unable to send input event\n");
+
 	return IRQ_HANDLED;
 }
 
-static int __devinit qpnp_pon_key_init(struct qpnp_pon *pon)
+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->spmi->dev, "Unable to send input event\n");
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t qpnp_resin_bark_irq(int irq, void *_pon)
+{
+	return IRQ_HANDLED;
+}
+
+static int __devinit
+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;
+	default:
+		return -EINVAL;
+	}
+
+	rc = qpnp_pon_masked_write(pon, QPNP_PON_PULL_CTL(pon->base),
+				pull_bit, cfg->pull_up ? pull_bit : 0);
+	if (rc)
+		dev_err(&pon->spmi->dev, "Unable to config pull-up\n");
+
+	return rc;
+}
+
+static int __devinit
+qpnp_config_reset(struct qpnp_pon *pon, struct qpnp_pon_config *cfg)
+{
+	int rc;
+	u8 i;
+	u16 s1_timer_addr, s2_cntl_addr, s2_timer_addr;
+
+	switch (cfg->pon_type) {
+	case PON_KPDPWR:
+		s1_timer_addr = QPNP_PON_KPDPWR_S1_TIMER(pon->base);
+		s2_timer_addr = QPNP_PON_KPDPWR_S2_TIMER(pon->base);
+		s2_cntl_addr = QPNP_PON_KPDPWR_S2_CNTL(pon->base);
+		break;
+	case PON_RESIN:
+		s1_timer_addr = QPNP_PON_RESIN_S1_TIMER(pon->base);
+		s2_timer_addr = QPNP_PON_RESIN_S2_TIMER(pon->base);
+		s2_cntl_addr = QPNP_PON_RESIN_S2_CNTL(pon->base);
+		break;
+	default:
+		return -EINVAL;
+	}
+	/* disable S2 reset */
+	rc = qpnp_pon_masked_write(pon, s2_cntl_addr,
+				QPNP_PON_S2_CNTL_EN, 0);
+	if (rc) {
+		dev_err(&pon->spmi->dev, "Unable to configure S2 enable\n");
+		return rc;
+	}
+
+	usleep(100);
+
+	/* 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->spmi->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->spmi->dev, "Unable to configure S2 timer\n");
+		return rc;
+	}
+
+	rc = qpnp_pon_masked_write(pon, s2_cntl_addr,
+				QPNP_PON_S2_CNTL_TYPE_MASK, (u8)cfg->s2_type);
+	if (rc) {
+		dev_err(&pon->spmi->dev, "Unable to configure S2 reset type\n");
+		return rc;
+	}
+
+	/* enable S2 reset */
+	rc = qpnp_pon_masked_write(pon, s2_cntl_addr,
+				QPNP_PON_S2_CNTL_EN, QPNP_PON_S2_CNTL_EN);
+	if (rc) {
+		dev_err(&pon->spmi->dev, "Unable to configure S2 enable\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+static int __devinit
+qpnp_pon_request_irqs(struct qpnp_pon *pon, struct qpnp_pon_config *cfg)
 {
 	int rc = 0;
-	u32 pullup, delay;
-	u8 pon_cntl;
 
-	pon->key_status_irq = spmi_get_irq_byname(pon->spmi,
-						NULL, "power-key");
-	if (pon->key_status_irq < 0) {
-		dev_err(&pon->spmi->dev, "Unable to get pon key irq\n");
-		return -ENXIO;
-	}
-
-	rc = of_property_read_u32(pon->spmi->dev.of_node,
-					"qcom,pon-key-dbc-delay", &delay);
-	if (rc) {
-		delay = (delay << 6) / USEC_PER_SEC;
-		delay = ilog2(delay);
-
-		rc = spmi_ext_register_readl(pon->spmi->ctrl, pon->spmi->sid,
-				QPNP_PON_DBC_CTL(pon->base), &pon_cntl, 1);
-		if (rc) {
-			dev_err(&pon->spmi->dev, "spmi read addr=%x failed\n",
-						QPNP_PON_DBC_CTL(pon->base));
-			return rc;
-		}
-		pon_cntl &= ~QPNP_PON_CNTL_TRIG_DELAY_MASK;
-		pon_cntl |= (delay & QPNP_PON_CNTL_TRIG_DELAY_MASK);
-		rc = spmi_ext_register_writel(pon->spmi->ctrl, pon->spmi->sid,
-				QPNP_PON_DBC_CTL(pon->base), &pon_cntl, 1);
-		if (rc) {
-			dev_err(&pon->spmi->dev, "spmi write addre=%x failed\n",
-						QPNP_PON_DBC_CTL(pon->base));
-			return rc;
-		}
-	}
-
-	rc = of_property_read_u32(pon->spmi->dev.of_node,
-				"qcom,pon-key-pull-up", &pullup);
-	if (!rc) {
-		rc = spmi_ext_register_readl(pon->spmi->ctrl, pon->spmi->sid,
-				QPNP_PON_PULL_CTL(pon->base), &pon_cntl, 1);
-		if (rc) {
-			dev_err(&pon->spmi->dev, "spmi read addr=%x failed\n",
-						QPNP_PON_PULL_CTL(pon->base));
-			return rc;
-		}
-		if (pullup)
-			pon_cntl |= QPNP_PON_CNTL_PULL_UP;
-		else
-			pon_cntl &= ~QPNP_PON_CNTL_PULL_UP;
-
-		rc = spmi_ext_register_writel(pon->spmi->ctrl, pon->spmi->sid,
-				QPNP_PON_PULL_CTL(pon->base), &pon_cntl, 1);
-		if (rc) {
-			dev_err(&pon->spmi->dev, "spmi write addr=%x failed\n",
-						QPNP_PON_PULL_CTL(pon->base));
-			return rc;
-		}
-	}
-
-	pon->pon_input = input_allocate_device();
-	if (!pon->pon_input) {
-		dev_err(&pon->spmi->dev, "Can't allocate pon button\n");
-		return -ENOMEM;
-	}
-
-	input_set_capability(pon->pon_input, EV_KEY, KEY_POWER);
-	pon->pon_input->name = "qpnp_pon_key";
-	pon->pon_input->phys = "qpnp_pon_key/input0";
-
-	rc = input_register_device(pon->pon_input);
-	if (rc) {
-		dev_err(&pon->spmi->dev, "Can't register pon key: %d\n", rc);
-		goto free_input_dev;
-	}
-
-	rc = request_any_context_irq(pon->key_status_irq, qpnp_pon_key_irq,
+	switch (cfg->pon_type) {
+	case PON_KPDPWR:
+		rc = devm_request_irq(&pon->spmi->dev, cfg->state_irq,
+							qpnp_kpdpwr_irq,
 				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
-						"qpnp_pon_key_status", pon);
-	if (rc < 0) {
-		dev_err(&pon->spmi->dev, "Can't request %d IRQ for pon: %d\n",
-						pon->key_status_irq, rc);
-		goto unreg_input_dev;
+						"qpnp_kpdpwr_status", pon);
+		if (rc < 0) {
+			dev_err(&pon->spmi->dev, "Can't request %d IRQ\n",
+							cfg->state_irq);
+			return rc;
+		}
+		if (cfg->support_reset) {
+			rc = devm_request_irq(&pon->spmi->dev, cfg->bark_irq,
+						qpnp_kpdpwr_bark_irq,
+						IRQF_TRIGGER_RISING,
+						"qpnp_kpdpwr_bark", pon);
+			if (rc < 0) {
+				dev_err(&pon->spmi->dev,
+					"Can't request %d IRQ\n",
+						cfg->bark_irq);
+				return rc;
+			}
+		}
+		break;
+	case PON_RESIN:
+		rc = devm_request_irq(&pon->spmi->dev, cfg->state_irq,
+							qpnp_resin_irq,
+				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+						"qpnp_resin_status", pon);
+		if (rc < 0) {
+			dev_err(&pon->spmi->dev, "Can't request %d IRQ\n",
+							cfg->state_irq);
+			return rc;
+		}
+		if (cfg->support_reset) {
+			rc = devm_request_irq(&pon->spmi->dev, cfg->bark_irq,
+						qpnp_resin_bark_irq,
+						IRQF_TRIGGER_RISING,
+						"qpnp_resin_bark", pon);
+			if (rc < 0) {
+				dev_err(&pon->spmi->dev,
+					"Can't request %d IRQ\n",
+						cfg->bark_irq);
+				return rc;
+			}
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return rc;
+}
+
+static int __devinit
+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->spmi->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 __devinit qpnp_pon_config_init(struct qpnp_pon *pon)
+{
+	int rc = 0, i = 0;
+	struct device_node *pp = NULL;
+	struct qpnp_pon_config *cfg;
+
+	/* iterate through the list of pon configs */
+	while ((pp = of_get_next_child(pon->spmi->dev.of_node, pp))) {
+
+		cfg = &pon->pon_cfg[i++];
+
+		rc = of_property_read_u32(pp, "qcom,pon-type", &cfg->pon_type);
+		if (rc) {
+			dev_err(&pon->spmi->dev, "PON type not specified\n");
+			return rc;
+		}
+
+		switch (cfg->pon_type) {
+		case PON_KPDPWR:
+			cfg->state_irq = spmi_get_irq_byname(pon->spmi,
+							NULL, "kpdpwr");
+			if (cfg->state_irq < 0) {
+				dev_err(&pon->spmi->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 && rc != -EINVAL) {
+				dev_err(&pon->spmi->dev,
+					"Unable to read 'support-reset'\n");
+				return rc;
+			}
+
+			if (cfg->support_reset) {
+				cfg->bark_irq = spmi_get_irq_byname(pon->spmi,
+							NULL, "kpdpwr-bark");
+				if (cfg->bark_irq < 0) {
+					dev_err(&pon->spmi->dev,
+					"Unable to get kpdpwr-bark irq\n");
+					return cfg->bark_irq;
+				}
+			}
+			break;
+		case PON_RESIN:
+			cfg->state_irq = spmi_get_irq_byname(pon->spmi,
+							NULL, "resin");
+			if (cfg->state_irq < 0) {
+				dev_err(&pon->spmi->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 && rc != -EINVAL) {
+				dev_err(&pon->spmi->dev,
+					"Unable to read 'support-reset'\n");
+				return rc;
+			}
+
+			if (cfg->support_reset) {
+				cfg->bark_irq = spmi_get_irq_byname(pon->spmi,
+							NULL, "resin-bark");
+				if (cfg->bark_irq < 0) {
+					dev_err(&pon->spmi->dev,
+					"Unable to get resin-bark irq\n");
+					return cfg->bark_irq;
+				}
+			}
+			break;
+		default:
+			dev_err(&pon->spmi->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->spmi->dev,
+					"Unable to read s1-timer\n");
+				return rc;
+			}
+			if (cfg->s1_timer > QPNP_PON_S1_TIMER_MAX) {
+				dev_err(&pon->spmi->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->spmi->dev,
+					"Unable to read s2-timer\n");
+				return rc;
+			}
+			if (cfg->s2_timer > QPNP_PON_S2_TIMER_MAX) {
+				dev_err(&pon->spmi->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->spmi->dev,
+					"Unable to read s2-type\n");
+				return rc;
+			}
+			if (cfg->s2_type > QPNP_PON_RESET_TYPE_MAX) {
+				dev_err(&pon->spmi->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->spmi->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->spmi->dev, "Unable to read pull-up\n");
+			return rc;
+		}
+	}
+
+	/* register the input device */
+	if (pon->pon_input) {
+		rc = input_register_device(pon->pon_input);
+		if (rc) {
+			dev_err(&pon->spmi->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->spmi->dev, "Unable to config pull-up\n");
+			goto unreg_input_dev;
+		}
+		/* Configure the reset-configuration */
+		if (cfg->support_reset) {
+			rc = qpnp_config_reset(pon, cfg);
+			if (rc) {
+				dev_err(&pon->spmi->dev,
+					"Unable to config pon reset\n");
+				goto unreg_input_dev;
+			}
+		}
+		rc = qpnp_pon_request_irqs(pon, cfg);
+		if (rc) {
+			dev_err(&pon->spmi->dev, "Unable to request-irq's\n");
+			goto unreg_input_dev;
+		}
 	}
 
 	device_init_wakeup(&pon->spmi->dev, 1);
-	enable_irq_wake(pon->key_status_irq);
 
 	return rc;
 
 unreg_input_dev:
-	input_unregister_device(pon->pon_input);
+	if (pon->pon_input)
+		input_unregister_device(pon->pon_input);
 free_input_dev:
-	input_free_device(pon->pon_input);
+	if (pon->pon_input)
+		input_free_device(pon->pon_input);
 	return rc;
 }
 
@@ -158,7 +562,8 @@
 {
 	struct qpnp_pon *pon;
 	struct resource *pon_resource;
-	u32 pon_key_enable = 0;
+	struct device_node *itr = NULL;
+	u32 delay = 0;
 	int rc = 0;
 
 	pon = devm_kzalloc(&spmi->dev, sizeof(struct qpnp_pon),
@@ -170,6 +575,20 @@
 
 	pon->spmi = spmi;
 
+	/* get the total number of pon configurations */
+	while ((itr = of_get_next_child(spmi->dev.of_node, itr)))
+		pon->num_pon_config++;
+
+	if (!pon->num_pon_config) {
+		/* No PON config., do not register the driver */
+		dev_err(&spmi->dev, "No PON config. specified\n");
+		return -EINVAL;
+	}
+
+	pon->pon_cfg = devm_kzalloc(&spmi->dev,
+			sizeof(struct qpnp_pon_config) * pon->num_pon_config,
+								GFP_KERNEL);
+
 	pon_resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
 	if (!pon_resource) {
 		dev_err(&spmi->dev, "Unable to get PON base address\n");
@@ -177,36 +596,41 @@
 	}
 	pon->base = pon_resource->start;
 
-	dev_set_drvdata(&spmi->dev, pon);
-
-	/* pon-key-enable property must be set to register pon key */
-	rc = of_property_read_u32(spmi->dev.of_node, "qcom,pon-key-enable",
-							&pon_key_enable);
+	rc = of_property_read_u32(pon->spmi->dev.of_node,
+				"qcom,pon-dbc-delay", &delay);
 	if (rc && rc != -EINVAL) {
-		dev_err(&spmi->dev,
-			"Error reading 'pon-key-enable' property (%d)", rc);
+		dev_err(&spmi->dev, "Unable to read debounce delay\n");
 		return rc;
-	}
-
-	if (pon_key_enable) {
-		rc = qpnp_pon_key_init(pon);
-		if (rc < 0) {
-			dev_err(&spmi->dev, "Failed to register pon-key\n");
+	} else {
+		delay = (delay << 6) / USEC_PER_SEC;
+		delay = ilog2(delay);
+		rc = qpnp_pon_masked_write(pon, QPNP_PON_DBC_CTL(pon->base),
+						QPNP_PON_DBC_DELAY_MASK, delay);
+		if (rc) {
+			dev_err(&spmi->dev, "Unable to set PON debounce\n");
 			return rc;
 		}
 	}
 
-	return 0;
+	dev_set_drvdata(&spmi->dev, pon);
+
+	/* register the PON configurations */
+	rc = qpnp_pon_config_init(pon);
+	if (rc) {
+		dev_err(&spmi->dev,
+			"Unable to intialize PON configurations\n");
+		return rc;
+	}
+
+	return rc;
 }
 
 static int qpnp_pon_remove(struct spmi_device *spmi)
 {
 	struct qpnp_pon *pon = dev_get_drvdata(&spmi->dev);
 
-	if (pon->pon_input) {
-		free_irq(pon->key_status_irq, pon);
+	if (pon->pon_input)
 		input_unregister_device(pon->pon_input);
-	}
 
 	return 0;
 }