mfd: pm8xxx-misc: Move reset_pwr_off for pm8058

Move the additional routines for pm8058_reset_pwr_off
from core to misc driver

Change-Id: I2346c812391d542dce0cd126a00cf9eda05d2494
Signed-off-by: Anirudh Ghayal <aghayal@codeaurora.org>
diff --git a/drivers/mfd/pm8xxx-misc.c b/drivers/mfd/pm8xxx-misc.c
index 7367e66..439cefe 100644
--- a/drivers/mfd/pm8xxx-misc.c
+++ b/drivers/mfd/pm8xxx-misc.c
@@ -32,9 +32,48 @@
 #define PON_CTRL_1_WD_EN_RESET			0x08
 #define PON_CTRL_1_WD_EN_PWR_OFF		0x00
 
-/* Regulator L22 control register */
+/* Regulator master enable addresses */
+#define REG_PM8058_VREG_EN_MSM			0x018
+#define REG_PM8058_VREG_EN_GRP_5_4		0x1C8
+
+/* Regulator control registers for shutdown/reset */
+#define REG_PM8058_S0_CTRL			0x004
+#define REG_PM8058_S1_CTRL			0x005
+#define REG_PM8058_S3_CTRL			0x111
+#define REG_PM8058_L21_CTRL			0x120
 #define REG_PM8058_L22_CTRL			0x121
 
+#define PM8058_REGULATOR_ENABLE_MASK		0x80
+#define PM8058_REGULATOR_ENABLE			0x80
+#define PM8058_REGULATOR_DISABLE		0x00
+#define PM8058_REGULATOR_PULL_DOWN_MASK		0x40
+#define PM8058_REGULATOR_PULL_DOWN_EN		0x40
+
+/* Buck CTRL register */
+#define PM8058_SMPS_LEGACY_VREF_SEL		0x20
+#define PM8058_SMPS_LEGACY_VPROG_MASK		0x1F
+#define PM8058_SMPS_ADVANCED_BAND_MASK		0xC0
+#define PM8058_SMPS_ADVANCED_BAND_SHIFT		6
+#define PM8058_SMPS_ADVANCED_VPROG_MASK		0x3F
+
+/* Buck TEST2 registers for shutdown/reset */
+#define REG_PM8058_S0_TEST2			0x084
+#define REG_PM8058_S1_TEST2			0x085
+#define REG_PM8058_S3_TEST2			0x11A
+
+#define PM8058_REGULATOR_BANK_WRITE		0x80
+#define PM8058_REGULATOR_BANK_MASK		0x70
+#define PM8058_REGULATOR_BANK_SHIFT		4
+#define PM8058_REGULATOR_BANK_SEL(n)	((n) << PM8058_REGULATOR_BANK_SHIFT)
+
+/* Buck TEST2 register bank 1 */
+#define PM8058_SMPS_LEGACY_VLOW_SEL		0x01
+
+/* Buck TEST2 register bank 7 */
+#define PM8058_SMPS_ADVANCED_MODE_MASK		0x02
+#define PM8058_SMPS_ADVANCED_MODE		0x02
+#define PM8058_SMPS_LEGACY_MODE			0x00
+
 /* SLEEP CTRL register */
 #define REG_PM8058_SLEEP_CTRL			0x02B
 #define REG_PM8921_SLEEP_CTRL			0x10A
@@ -89,6 +128,154 @@
 	return rc;
 }
 
+/*
+ * Set an SMPS regulator to be disabled in its CTRL register, but enabled
+ * in the master enable register.  Also set it's pull down enable bit.
+ * Take care to make sure that the output voltage doesn't change if switching
+ * from advanced mode to legacy mode.
+ */
+static int
+__pm8058_disable_smps_locally_set_pull_down(struct pm8xxx_misc_chip *chip,
+	u16 ctrl_addr, u16 test2_addr, u16 master_enable_addr,
+	u8 master_enable_bit)
+{
+	int rc = 0;
+	u8 vref_sel, vlow_sel, band, vprog, bank, reg;
+
+	bank = PM8058_REGULATOR_BANK_SEL(7);
+	rc = pm8xxx_writeb(chip->dev->parent, test2_addr, bank);
+	if (rc) {
+		pr_err("%s: pm8xxx_writeb(0x%03X) failed: rc=%d\n", __func__,
+			test2_addr, rc);
+		goto done;
+	}
+
+	rc = pm8xxx_readb(chip->dev->parent, test2_addr, &reg);
+	if (rc) {
+		pr_err("%s: FAIL pm8xxx_readb(0x%03X): rc=%d\n",
+		       __func__, test2_addr, rc);
+		goto done;
+	}
+
+	/* Check if in advanced mode. */
+	if ((reg & PM8058_SMPS_ADVANCED_MODE_MASK) ==
+					PM8058_SMPS_ADVANCED_MODE) {
+		/* Determine current output voltage. */
+		rc = pm8xxx_readb(chip->dev->parent, ctrl_addr, &reg);
+		if (rc) {
+			pr_err("%s: FAIL pm8xxx_readb(0x%03X): rc=%d\n",
+			       __func__, ctrl_addr, rc);
+			goto done;
+		}
+
+		band = (reg & PM8058_SMPS_ADVANCED_BAND_MASK)
+			>> PM8058_SMPS_ADVANCED_BAND_SHIFT;
+		switch (band) {
+		case 3:
+			vref_sel = 0;
+			vlow_sel = 0;
+			break;
+		case 2:
+			vref_sel = PM8058_SMPS_LEGACY_VREF_SEL;
+			vlow_sel = 0;
+			break;
+		case 1:
+			vref_sel = PM8058_SMPS_LEGACY_VREF_SEL;
+			vlow_sel = PM8058_SMPS_LEGACY_VLOW_SEL;
+			break;
+		default:
+			pr_err("%s: regulator already disabled\n", __func__);
+			return -EPERM;
+		}
+		vprog = (reg & PM8058_SMPS_ADVANCED_VPROG_MASK);
+		/* Round up if fine step is in use. */
+		vprog = (vprog + 1) >> 1;
+		if (vprog > PM8058_SMPS_LEGACY_VPROG_MASK)
+			vprog = PM8058_SMPS_LEGACY_VPROG_MASK;
+
+		/* Set VLOW_SEL bit. */
+		bank = PM8058_REGULATOR_BANK_SEL(1);
+		rc = pm8xxx_writeb(chip->dev->parent, test2_addr, bank);
+		if (rc) {
+			pr_err("%s: FAIL pm8xxx_writeb(0x%03X): rc=%d\n",
+			       __func__, test2_addr, rc);
+			goto done;
+		}
+
+		rc = pm8xxx_misc_masked_write(chip, test2_addr,
+			PM8058_REGULATOR_BANK_WRITE | PM8058_REGULATOR_BANK_MASK
+				| PM8058_SMPS_LEGACY_VLOW_SEL,
+			PM8058_REGULATOR_BANK_WRITE |
+			PM8058_REGULATOR_BANK_SEL(1) | vlow_sel);
+		if (rc)
+			goto done;
+
+		/* Switch to legacy mode */
+		bank = PM8058_REGULATOR_BANK_SEL(7);
+		rc = pm8xxx_writeb(chip->dev->parent, test2_addr, bank);
+		if (rc) {
+			pr_err("%s: FAIL pm8xxx_writeb(0x%03X): rc=%d\n",
+					__func__, test2_addr, rc);
+			goto done;
+		}
+		rc = pm8xxx_misc_masked_write(chip, test2_addr,
+				PM8058_REGULATOR_BANK_WRITE |
+				PM8058_REGULATOR_BANK_MASK |
+				PM8058_SMPS_ADVANCED_MODE_MASK,
+				PM8058_REGULATOR_BANK_WRITE |
+				PM8058_REGULATOR_BANK_SEL(7) |
+				PM8058_SMPS_LEGACY_MODE);
+		if (rc)
+			goto done;
+
+		/* Enable locally, enable pull down, keep voltage the same. */
+		rc = pm8xxx_misc_masked_write(chip, ctrl_addr,
+			PM8058_REGULATOR_ENABLE_MASK |
+			PM8058_REGULATOR_PULL_DOWN_MASK |
+			PM8058_SMPS_LEGACY_VREF_SEL |
+			PM8058_SMPS_LEGACY_VPROG_MASK,
+			PM8058_REGULATOR_ENABLE | PM8058_REGULATOR_PULL_DOWN_EN
+				| vref_sel | vprog);
+		if (rc)
+			goto done;
+	}
+
+	/* Enable in master control register. */
+	rc = pm8xxx_misc_masked_write(chip, master_enable_addr,
+			master_enable_bit, master_enable_bit);
+	if (rc)
+		goto done;
+
+	/* Disable locally and enable pull down. */
+	rc = pm8xxx_misc_masked_write(chip, ctrl_addr,
+		PM8058_REGULATOR_ENABLE_MASK | PM8058_REGULATOR_PULL_DOWN_MASK,
+		PM8058_REGULATOR_DISABLE | PM8058_REGULATOR_PULL_DOWN_EN);
+
+done:
+	return rc;
+}
+
+static int
+__pm8058_disable_ldo_locally_set_pull_down(struct pm8xxx_misc_chip *chip,
+		u16 ctrl_addr, u16 master_enable_addr, u8 master_enable_bit)
+{
+	int rc;
+
+	/* Enable LDO in master control register. */
+	rc = pm8xxx_misc_masked_write(chip, master_enable_addr,
+			master_enable_bit, master_enable_bit);
+	if (rc)
+		goto done;
+
+	/* Disable LDO in CTRL register and set pull down */
+	rc = pm8xxx_misc_masked_write(chip, ctrl_addr,
+		PM8058_REGULATOR_ENABLE_MASK | PM8058_REGULATOR_PULL_DOWN_MASK,
+		PM8058_REGULATOR_DISABLE | PM8058_REGULATOR_PULL_DOWN_EN);
+
+done:
+	return rc;
+}
+
 static int __pm8018_reset_pwr_off(struct pm8xxx_misc_chip *chip, int reset)
 {
 	int rc;
@@ -122,6 +309,24 @@
 {
 	int rc;
 
+	/* When shutting down, enable active pulldowns on important rails. */
+	if (!reset) {
+		/* Disable SMPS's 0,1,3 locally and set pulldown enable bits. */
+		__pm8058_disable_smps_locally_set_pull_down(chip,
+			REG_PM8058_S0_CTRL, REG_PM8058_S0_TEST2,
+			REG_PM8058_VREG_EN_MSM, BIT(7));
+		__pm8058_disable_smps_locally_set_pull_down(chip,
+			REG_PM8058_S1_CTRL, REG_PM8058_S1_TEST2,
+			REG_PM8058_VREG_EN_MSM, BIT(6));
+		__pm8058_disable_smps_locally_set_pull_down(chip,
+			REG_PM8058_S3_CTRL, REG_PM8058_S3_TEST2,
+			REG_PM8058_VREG_EN_GRP_5_4, BIT(7) | BIT(4));
+		/* Disable LDO 21 locally and set pulldown enable bit. */
+		__pm8058_disable_ldo_locally_set_pull_down(chip,
+			REG_PM8058_L21_CTRL, REG_PM8058_VREG_EN_GRP_5_4,
+			BIT(1));
+	}
+
 	/*
 	 * Fix-up: Set regulator LDO22 to 1.225 V in high power mode. Leave its
 	 * pull-down state intact. This ensures a safe shutdown.