mfd: pm8058: Locally disable regulators S0/1/3 and L21 during shutdown
When shutting down or restarting, PMIC 8058 regulator S0, S1, S3 and
L21 need to be managed manually. Since the master enable bits for these
regulators are kept set, the local enable bits should be unset and the
active pull-down must be enabled. Any regulators in advanced mode
should be disabled as well. This regulator state ensures that
these regulators drops as expected when PS_HOLD is dropped.
CRs-Fixed: 295731
Signed-off-by: David Collins <collinsd@codeaurora.org>
Signed-off-by: Willie Ruan <wruan@codeaurora.org>
diff --git a/drivers/mfd/pmic8058.c b/drivers/mfd/pmic8058.c
index 06a725e..79c2a9f 100644
--- a/drivers/mfd/pmic8058.c
+++ b/drivers/mfd/pmic8058.c
@@ -16,6 +16,7 @@
*/
#include <linux/interrupt.h>
#include <linux/i2c.h>
+#include <linux/bitops.h>
#include <linux/slab.h>
#include <linux/ratelimit.h>
#include <linux/kthread.h>
@@ -77,9 +78,49 @@
#define SSBI_REG_ADDR_PON_CNTL_5 0x7B
#define PM8058_HARD_RESET_EN_MASK 0x08
-/* Regulator L22 control register */
+/* Regulator master enable addresses */
+#define SSBI_REG_ADDR_VREG_EN_MSM 0x018
+#define SSBI_REG_ADDR_VREG_EN_GRP_5_4 0x1C8
+
+/* Regulator control registers for shutdown/reset */
+#define SSBI_REG_ADDR_S0_CTRL 0x004
+#define SSBI_REG_ADDR_S1_CTRL 0x005
+#define SSBI_REG_ADDR_S3_CTRL 0x111
+#define SSBI_REG_ADDR_L21_CTRL 0x120
#define SSBI_REG_ADDR_L22_CTRL 0x121
+#define REGULATOR_ENABLE_MASK 0x80
+#define REGULATOR_ENABLE 0x80
+#define REGULATOR_DISABLE 0x00
+#define REGULATOR_PULL_DOWN_MASK 0x40
+#define REGULATOR_PULL_DOWN_EN 0x40
+#define REGULATOR_PULL_DOWN_DIS 0x00
+
+/* Buck CTRL register */
+#define SMPS_LEGACY_VREF_SEL 0x20
+#define SMPS_LEGACY_VPROG_MASK 0x1F
+#define SMPS_ADVANCED_BAND_MASK 0xC0
+#define SMPS_ADVANCED_BAND_SHIFT 6
+#define SMPS_ADVANCED_VPROG_MASK 0x3F
+
+/* Buck TEST2 registers for shutdown/reset */
+#define SSBI_REG_ADDR_S0_TEST2 0x084
+#define SSBI_REG_ADDR_S1_TEST2 0x085
+#define SSBI_REG_ADDR_S3_TEST2 0x11A
+
+#define REGULATOR_BANK_WRITE 0x80
+#define REGULATOR_BANK_MASK 0x70
+#define REGULATOR_BANK_SHIFT 4
+#define REGULATOR_BANK_SEL(n) ((n) << REGULATOR_BANK_SHIFT)
+
+/* Buck TEST2 register bank 1 */
+#define SMPS_LEGACY_VLOW_SEL 0x01
+
+/* Buck TEST2 register bank 7 */
+#define SMPS_ADVANCED_MODE_MASK 0x02
+#define SMPS_ADVANCED_MODE 0x02
+#define SMPS_LEGACY_MODE 0x00
+
/* SLEEP CNTL register */
#define SSBI_REG_ADDR_SLEEP_CNTL 0x02B
@@ -362,16 +403,171 @@
}
EXPORT_SYMBOL(pm8058_watchdog_reset_control);
-int pm8058_reset_pwr_off(int reset)
+/*
+ * 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 disable_smps_locally_set_pull_down(u16 ctrl_addr, u16 test2_addr,
+ u16 master_enable_addr, u8 master_enable_bit)
{
- int rc;
- u8 pon;
- u8 ctrl;
- u8 smpl;
+ int rc = 0;
+ u8 vref_sel, vlow_sel, band, vprog, bank, reg;
if (pmic_chip == NULL)
return -ENODEV;
+ bank = REGULATOR_BANK_SEL(7);
+ rc = ssbi_write(pmic_chip->dev, test2_addr, &bank, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_write(0x%03X): rc=%d\n", __func__,
+ test2_addr, rc);
+ goto done;
+ }
+
+ rc = ssbi_read(pmic_chip->dev, test2_addr, ®, 1);
+ if (rc) {
+ pr_err("%s: FAIL pm8058_read(0x%03X): rc=%d\n",
+ __func__, test2_addr, rc);
+ goto done;
+ }
+
+ /* Check if in advanced mode. */
+ if ((reg & SMPS_ADVANCED_MODE_MASK) == SMPS_ADVANCED_MODE) {
+ /* Determine current output voltage. */
+ rc = ssbi_read(pmic_chip->dev, ctrl_addr, ®, 1);
+ if (rc) {
+ pr_err("%s: FAIL pm8058_read(0x%03X): rc=%d\n",
+ __func__, ctrl_addr, rc);
+ goto done;
+ }
+
+ band = (reg & SMPS_ADVANCED_BAND_MASK)
+ >> SMPS_ADVANCED_BAND_SHIFT;
+ switch (band) {
+ case 3:
+ vref_sel = 0;
+ vlow_sel = 0;
+ break;
+ case 2:
+ vref_sel = SMPS_LEGACY_VREF_SEL;
+ vlow_sel = 0;
+ break;
+ case 1:
+ vref_sel = SMPS_LEGACY_VREF_SEL;
+ vlow_sel = SMPS_LEGACY_VLOW_SEL;
+ break;
+ default:
+ pr_err("%s: regulator already disabled\n", __func__);
+ return -EPERM;
+ }
+ vprog = (reg & SMPS_ADVANCED_VPROG_MASK);
+ /* Round up if fine step is in use. */
+ vprog = (vprog + 1) >> 1;
+ if (vprog > SMPS_LEGACY_VPROG_MASK)
+ vprog = SMPS_LEGACY_VPROG_MASK;
+
+ /* Set VLOW_SEL bit. */
+ bank = REGULATOR_BANK_SEL(1);
+ rc = ssbi_write(pmic_chip->dev, test2_addr, &bank, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_write(0x%03X): rc=%d\n",
+ __func__, test2_addr, rc);
+ goto done;
+ }
+ rc = pm8058_masked_write(test2_addr,
+ REGULATOR_BANK_WRITE | REGULATOR_BANK_SEL(1)
+ | vlow_sel,
+ REGULATOR_BANK_WRITE | REGULATOR_BANK_MASK
+ | SMPS_LEGACY_VLOW_SEL);
+ if (rc)
+ goto done;
+
+ /* Switch to legacy mode */
+ bank = REGULATOR_BANK_SEL(7);
+ rc = ssbi_write(pmic_chip->dev, test2_addr, &bank, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_write(0x%03X): rc=%d\n", __func__,
+ test2_addr, rc);
+ goto done;
+ }
+ rc = pm8058_masked_write(test2_addr,
+ REGULATOR_BANK_WRITE | REGULATOR_BANK_SEL(7)
+ | SMPS_LEGACY_MODE,
+ REGULATOR_BANK_WRITE | REGULATOR_BANK_MASK
+ | SMPS_ADVANCED_MODE_MASK);
+ if (rc)
+ goto done;
+
+ /* Enable locally, enable pull down, keep voltage the same. */
+ rc = pm8058_masked_write(ctrl_addr,
+ REGULATOR_ENABLE | REGULATOR_PULL_DOWN_EN
+ | vref_sel | vprog,
+ REGULATOR_ENABLE_MASK | REGULATOR_PULL_DOWN_MASK
+ | SMPS_LEGACY_VREF_SEL | SMPS_LEGACY_VPROG_MASK);
+ if (rc)
+ goto done;
+ }
+
+ /* Enable in master control register. */
+ rc = pm8058_masked_write(master_enable_addr, master_enable_bit,
+ master_enable_bit);
+ if (rc)
+ goto done;
+
+ /* Disable locally and enable pull down. */
+ rc = pm8058_masked_write(ctrl_addr,
+ REGULATOR_DISABLE | REGULATOR_PULL_DOWN_EN,
+ REGULATOR_ENABLE_MASK | REGULATOR_PULL_DOWN_MASK);
+
+done:
+ return rc;
+}
+
+static int disable_ldo_locally_set_pull_down(u16 ctrl_addr,
+ u16 master_enable_addr, u8 master_enable_bit)
+{
+ int rc;
+
+ /* Enable LDO in master control register. */
+ rc = pm8058_masked_write(master_enable_addr, master_enable_bit,
+ master_enable_bit);
+ if (rc)
+ goto done;
+
+ /* Disable LDO in CTRL register and set pull down */
+ rc = pm8058_masked_write(ctrl_addr,
+ REGULATOR_DISABLE | REGULATOR_PULL_DOWN_EN,
+ REGULATOR_ENABLE_MASK | REGULATOR_PULL_DOWN_MASK);
+
+done:
+ return rc;
+}
+
+int pm8058_reset_pwr_off(int reset)
+{
+ int rc;
+ u8 pon, ctrl, smpl;
+
+ if (pmic_chip == NULL)
+ return -ENODEV;
+
+ /* When shutting down, enable active pulldowns on important rails. */
+ if (!reset) {
+ /* Disable SMPS's 0,1,3 locally and set pulldown enable bits. */
+ disable_smps_locally_set_pull_down(SSBI_REG_ADDR_S0_CTRL,
+ SSBI_REG_ADDR_S0_TEST2, SSBI_REG_ADDR_VREG_EN_MSM, BIT(7));
+ disable_smps_locally_set_pull_down(SSBI_REG_ADDR_S1_CTRL,
+ SSBI_REG_ADDR_S1_TEST2, SSBI_REG_ADDR_VREG_EN_MSM, BIT(6));
+ disable_smps_locally_set_pull_down(SSBI_REG_ADDR_S3_CTRL,
+ SSBI_REG_ADDR_S3_TEST2, SSBI_REG_ADDR_VREG_EN_GRP_5_4,
+ BIT(7) | BIT(4));
+ /* Disable LDO 21 locally and set pulldown enable bit. */
+ disable_ldo_locally_set_pull_down(SSBI_REG_ADDR_L21_CTRL,
+ SSBI_REG_ADDR_VREG_EN_GRP_5_4, BIT(1));
+ }
+
/* Set regulator L22 to 1.225V in high power mode. */
rc = ssbi_read(pmic_chip->dev, SSBI_REG_ADDR_L22_CTRL, &ctrl, 1);
if (rc) {