]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
regulator: pfuze100-regulator: provide pm_power_off_prepare handler
authorOleksij Rempel <o.rempel@pengutronix.de>
Thu, 2 Aug 2018 10:34:24 +0000 (12:34 +0200)
committerMark Brown <broonie@kernel.org>
Tue, 11 Sep 2018 15:15:57 +0000 (16:15 +0100)
On some boards the SoC can use one pin "PMIC_STBY_REQ" to notify th PMIC
about state changes. In this case internal state of PMIC must be
preconfigured for upcomming state change.
It works fine with the current regulator framework, except with the
power-off case.

This patch is providing an optional pm_power_off_prepare handler
which will configure standby state of the PMIC to disable all power lines.

In my power consumption test on RIoTBoard, I got the following results:
power off without this patch: 320 mA
power off with this patch: 2   mA
suspend to ram: 40  mA

Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/regulator/pfuze100-regulator.c

index 31c3a236120a80909ede26f29caa5288011d8cbf..1e43a9de3bd53507cd7be22e3a2ed9f7d236104c 100644 (file)
 
 #define PFUZE100_COINVOL       0x1a
 #define PFUZE100_SW1ABVOL      0x20
+#define PFUZE100_SW1ABMODE     0x23
 #define PFUZE100_SW1CVOL       0x2e
+#define PFUZE100_SW1CMODE      0x31
 #define PFUZE100_SW2VOL                0x35
+#define PFUZE100_SW2MODE       0x38
 #define PFUZE100_SW3AVOL       0x3c
+#define PFUZE100_SW3AMODE      0x3f
 #define PFUZE100_SW3BVOL       0x43
+#define PFUZE100_SW3BMODE      0x46
 #define PFUZE100_SW4VOL                0x4a
+#define PFUZE100_SW4MODE       0x4d
 #define PFUZE100_SWBSTCON1     0x66
 #define PFUZE100_VREFDDRCON    0x6a
 #define PFUZE100_VSNVSVOL      0x6b
 #define PFUZE100_VGEN5VOL      0x70
 #define PFUZE100_VGEN6VOL      0x71
 
+#define PFUZE100_SWxMODE_MASK  0xf
+#define PFUZE100_SWxMODE_APS_APS       0x8
+#define PFUZE100_SWxMODE_APS_OFF       0x4
+
+#define PFUZE100_VGENxLPWR     BIT(6)
+#define PFUZE100_VGENxSTBY     BIT(5)
+
 enum chips { PFUZE100, PFUZE200, PFUZE3000 = 3, PFUZE3001 = 0x31, };
 
 struct pfuze_regulator {
@@ -559,6 +572,69 @@ static inline struct device_node *match_of_node(int index)
 }
 #endif
 
+static struct pfuze_chip *syspm_pfuze_chip;
+
+static void pfuze_power_off_prepare(void)
+{
+       dev_info(syspm_pfuze_chip->dev, "Configure standy mode for power off");
+
+       /* Switch from default mode: APS/APS to APS/Off */
+       regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_SW1ABMODE,
+                          PFUZE100_SWxMODE_MASK, PFUZE100_SWxMODE_APS_OFF);
+       regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_SW1CMODE,
+                          PFUZE100_SWxMODE_MASK, PFUZE100_SWxMODE_APS_OFF);
+       regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_SW2MODE,
+                          PFUZE100_SWxMODE_MASK, PFUZE100_SWxMODE_APS_OFF);
+       regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_SW3AMODE,
+                          PFUZE100_SWxMODE_MASK, PFUZE100_SWxMODE_APS_OFF);
+       regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_SW3BMODE,
+                          PFUZE100_SWxMODE_MASK, PFUZE100_SWxMODE_APS_OFF);
+       regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_SW4MODE,
+                          PFUZE100_SWxMODE_MASK, PFUZE100_SWxMODE_APS_OFF);
+
+       regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_VGEN1VOL,
+                          PFUZE100_VGENxLPWR | PFUZE100_VGENxSTBY,
+                          PFUZE100_VGENxSTBY);
+       regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_VGEN2VOL,
+                          PFUZE100_VGENxLPWR | PFUZE100_VGENxSTBY,
+                          PFUZE100_VGENxSTBY);
+       regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_VGEN3VOL,
+                          PFUZE100_VGENxLPWR | PFUZE100_VGENxSTBY,
+                          PFUZE100_VGENxSTBY);
+       regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_VGEN4VOL,
+                          PFUZE100_VGENxLPWR | PFUZE100_VGENxSTBY,
+                          PFUZE100_VGENxSTBY);
+       regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_VGEN5VOL,
+                          PFUZE100_VGENxLPWR | PFUZE100_VGENxSTBY,
+                          PFUZE100_VGENxSTBY);
+       regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_VGEN6VOL,
+                          PFUZE100_VGENxLPWR | PFUZE100_VGENxSTBY,
+                          PFUZE100_VGENxSTBY);
+}
+
+static int pfuze_power_off_prepare_init(struct pfuze_chip *pfuze_chip)
+{
+       if (pfuze_chip->chip_id != PFUZE100) {
+               dev_warn(pfuze_chip->dev, "Requested pm_power_off_prepare handler for not supported chip\n");
+               return -ENODEV;
+       }
+
+       if (pm_power_off_prepare) {
+               dev_warn(pfuze_chip->dev, "pm_power_off_prepare is already registered.\n");
+               return -EBUSY;
+       }
+
+       if (syspm_pfuze_chip) {
+               dev_warn(pfuze_chip->dev, "syspm_pfuze_chip is already set.\n");
+               return -EBUSY;
+       }
+
+       syspm_pfuze_chip = pfuze_chip;
+       pm_power_off_prepare = pfuze_power_off_prepare;
+
+       return 0;
+}
+
 static int pfuze_identify(struct pfuze_chip *pfuze_chip)
 {
        unsigned int value;
@@ -753,6 +829,20 @@ static int pfuze100_regulator_probe(struct i2c_client *client,
                }
        }
 
+       if (of_property_read_bool(client->dev.of_node,
+                                 "fsl,pmic-stby-poweroff"))
+               return pfuze_power_off_prepare_init(pfuze_chip);
+
+       return 0;
+}
+
+static int pfuze100_regulator_remove(struct i2c_client *client)
+{
+       if (syspm_pfuze_chip) {
+               syspm_pfuze_chip = NULL;
+               pm_power_off_prepare = NULL;
+       }
+
        return 0;
 }
 
@@ -763,6 +853,7 @@ static struct i2c_driver pfuze_driver = {
                .of_match_table = pfuze_dt_ids,
        },
        .probe = pfuze100_regulator_probe,
+       .remove = pfuze100_regulator_remove,
 };
 module_i2c_driver(pfuze_driver);