Merge "msm: qpnp-power-on: Add API to reset and shutdown the system"
diff --git a/Documentation/devicetree/bindings/platform/msm/qpnp-power-on.txt b/Documentation/devicetree/bindings/platform/msm/qpnp-power-on.txt
index 46b39ec..21d376a 100644
--- a/Documentation/devicetree/bindings/platform/msm/qpnp-power-on.txt
+++ b/Documentation/devicetree/bindings/platform/msm/qpnp-power-on.txt
@@ -13,7 +13,7 @@
 - interrupts:	Specifies the interrupt associated with PON.
 
 Optional properties:
-- qcom,pon-dbc-delay:		The debouce delay for the power-key interrupt
+- qcom,pon-dbc-delay		The debouce delay for the power-key interrupt
 				specifed in us. The value ranges from 2 seconds
 				to 1/64 of a second. Possible values are -
 				- 2, 1, 1/2, 1/4, 1/8, 1/16, 1/32, 1/64
@@ -24,11 +24,15 @@
 				reset source. All the child nodes are optional,
 				if none of them are 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.
 
 All the below properties are in the sub-node section (properties of the child
 node).
 
-- qcom,pull-up:			The initial state of the reset pin under
+- qcom,pull-up			The initial state of the reset pin under
 				consideration.
 				0 = No pull-up
 				1 = pull-up enabled
diff --git a/drivers/platform/msm/qpnp-power-on.c b/drivers/platform/msm/qpnp-power-on.c
index 996456f..33b12ae 100644
--- a/drivers/platform/msm/qpnp-power-on.c
+++ b/drivers/platform/msm/qpnp-power-on.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, 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
@@ -35,6 +35,7 @@
 #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_PS_HOLD_RST_CTL(base)	(base + 0x5A)
 
 #define QPNP_PON_RESIN_PULL_UP		BIT(0)
 #define QPNP_PON_KPDPWR_PULL_UP		BIT(1)
@@ -50,6 +51,10 @@
 #define QPNP_PON_RESIN_N_SET		BIT(1)
 #define QPNP_PON_RESIN_BARK_N_SET	BIT(4)
 
+#define QPNP_PON_RESET_EN		BIT(7)
+#define QPNP_PON_WARM_RESET		BIT(0)
+#define QPNP_PON_SHUTDOWN		BIT(2)
+
 /* Ranges */
 #define QPNP_PON_S1_TIMER_MAX		10256
 #define QPNP_PON_S2_TIMER_MAX		2000
@@ -84,6 +89,8 @@
 	struct delayed_work bark_work;
 };
 
+static struct qpnp_pon *sys_reset_dev;
+
 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
@@ -113,6 +120,57 @@
 	return rc;
 }
 
+/**
+ * qpnp_pon_system_pwr_off - Configure system-reset PMIC for shutdown or reset
+ * @reset: Configures for shutdown if 0, or reset if 1.
+ *
+ * This function will only configure a single PMIC. The other PMICs in the
+ * system are slaved off of it and require no explicit configuration. Once
+ * the system-reset PMIC is configured properly, the MSM can drop PS_HOLD to
+ * activate the specified configuration.
+ */
+int qpnp_pon_system_pwr_off(bool reset)
+{
+	int rc;
+	struct qpnp_pon *pon = sys_reset_dev;
+
+	if (!pon)
+		return -ENODEV;
+
+	rc = qpnp_pon_masked_write(pon, QPNP_PON_PS_HOLD_RST_CTL(pon->base),
+							QPNP_PON_RESET_EN, 0);
+	if (rc)
+		dev_err(&pon->spmi->dev,
+			"Unable to write to addr=%x, rc(%d)\n",
+				QPNP_PON_PS_HOLD_RST_CTL(pon->base), 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->base),
+			   QPNP_PON_WARM_RESET | QPNP_PON_SHUTDOWN,
+			   reset ? QPNP_PON_WARM_RESET : QPNP_PON_SHUTDOWN);
+	if (rc)
+		dev_err(&pon->spmi->dev,
+			"Unable to write to addr=%x, rc(%d)\n",
+				QPNP_PON_PS_HOLD_RST_CTL(pon->base), rc);
+
+	rc = qpnp_pon_masked_write(pon, QPNP_PON_PS_HOLD_RST_CTL(pon->base),
+							QPNP_PON_RESET_EN,
+							QPNP_PON_RESET_EN);
+	if (rc)
+		dev_err(&pon->spmi->dev,
+			"Unable to write to addr=%x, rc(%d)\n",
+				QPNP_PON_PS_HOLD_RST_CTL(pon->base), rc);
+
+	return rc;
+}
+EXPORT_SYMBOL(qpnp_pon_system_pwr_off);
+
 static struct qpnp_pon_config *
 qpnp_get_cfg(struct qpnp_pon *pon, u32 pon_type)
 {
@@ -662,7 +720,7 @@
 	struct resource *pon_resource;
 	struct device_node *itr = NULL;
 	u32 delay = 0;
-	int rc = 0;
+	int rc, sys_reset;
 
 	pon = devm_kzalloc(&spmi->dev, sizeof(struct qpnp_pon),
 							GFP_KERNEL);
@@ -671,6 +729,15 @@
 		return -ENOMEM;
 	}
 
+	sys_reset = of_property_read_bool(spmi->dev.of_node,
+						"qcom,system-reset");
+	if (sys_reset && sys_reset_dev) {
+		dev_err(&spmi->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->spmi = spmi;
 
 	/* get the total number of pon configurations */
diff --git a/include/linux/qpnp/power-on.h b/include/linux/qpnp/power-on.h
new file mode 100644
index 0000000..85dbce9
--- /dev/null
+++ b/include/linux/qpnp/power-on.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2012, 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>
+
+#ifdef CONFIG_QPNP_POWER_ON
+int qpnp_pon_system_pwr_off(bool reset);
+#else
+static int qpnp_pon_system_pwr_off(bool reset) { return -ENODEV; }
+#endif
+
+#endif