power: suppy: ucs1002: disable power when max current is 0
authorLucas Stach <l.stach@pengutronix.de>
Tue, 19 Nov 2019 17:00:04 +0000 (18:00 +0100)
committerSebastian Reichel <sre@kernel.org>
Thu, 19 Dec 2019 00:07:53 +0000 (01:07 +0100)
For some devices userspace needs the ability to completely cut the power
to the USB devices connected to the charge controller. An easy way to
achieve this is by allowing 0 as a valid max current and forcibly disable
the output in that case, as well as enable it again if the regulator is
in use and a non-0 max current is set.

Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
Tested-by: Chris Healy <cphealy@gmail.com>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
drivers/power/supply/ucs1002_power.c

index 1b80ae479e7da8e525ab14196a5d38ff1c8b5cd1..0ca80d00b80aa37522d9b70453cbbe8763a6562d 100644 (file)
@@ -100,7 +100,9 @@ struct ucs1002_info {
        struct i2c_client *client;
        struct regmap *regmap;
        struct regulator_desc *regulator_descriptor;
+       struct regulator_dev *rdev;
        bool present;
+       bool output_disable;
 };
 
 static enum power_supply_property ucs1002_props[] = {
@@ -233,6 +235,11 @@ static int ucs1002_get_max_current(struct ucs1002_info *info,
        unsigned int reg;
        int ret;
 
+       if (info->output_disable) {
+               val->intval = 0;
+               return 0;
+       }
+
        ret = regmap_read(info->regmap, UCS1002_REG_ILIMIT, &reg);
        if (ret)
                return ret;
@@ -247,6 +254,12 @@ static int ucs1002_set_max_current(struct ucs1002_info *info, u32 val)
        unsigned int reg;
        int ret, idx;
 
+       if (val == 0) {
+               info->output_disable = true;
+               regulator_disable_regmap(info->rdev);
+               return 0;
+       }
+
        for (idx = 0; idx < ARRAY_SIZE(ucs1002_current_limit_uA); idx++) {
                if (val == ucs1002_current_limit_uA[idx])
                        break;
@@ -270,6 +283,12 @@ static int ucs1002_set_max_current(struct ucs1002_info *info, u32 val)
        if (reg != idx)
                return -EINVAL;
 
+       info->output_disable = false;
+
+       if (info->rdev && info->rdev->use_count &&
+           !regulator_is_enabled_regmap(info->rdev))
+               regulator_enable_regmap(info->rdev);
+
        return 0;
 }
 
@@ -470,9 +489,24 @@ static irqreturn_t ucs1002_alert_irq(int irq, void *data)
        return IRQ_HANDLED;
 }
 
+int ucs1002_regulator_enable(struct regulator_dev *rdev)
+{
+       struct ucs1002_info *info = rdev_get_drvdata(rdev);
+
+       /*
+        * If the output is disabled due to 0 maximum current, just pretend the
+        * enable did work. The regulator will be enabled as soon as we get a
+        * a non-zero maximum current budget.
+        */
+       if (info->output_disable)
+               return 0;
+
+       return regulator_enable_regmap(rdev);
+}
+
 static const struct regulator_ops ucs1002_regulator_ops = {
        .is_enabled     = regulator_is_enabled_regmap,
-       .enable         = regulator_enable_regmap,
+       .enable         = ucs1002_regulator_enable,
        .disable        = regulator_disable_regmap,
 };
 
@@ -499,7 +533,6 @@ static int ucs1002_probe(struct i2c_client *client,
        };
        struct regulator_config regulator_config = {};
        int irq_a_det, irq_alert, ret;
-       struct regulator_dev *rdev;
        struct ucs1002_info *info;
        unsigned int regval;
 
@@ -589,10 +622,11 @@ static int ucs1002_probe(struct i2c_client *client,
        regulator_config.dev = dev;
        regulator_config.of_node = dev->of_node;
        regulator_config.regmap = info->regmap;
+       regulator_config.driver_data = info;
 
-       rdev = devm_regulator_register(dev, info->regulator_descriptor,
+       info->rdev = devm_regulator_register(dev, info->regulator_descriptor,
                                       &regulator_config);
-       ret = PTR_ERR_OR_ZERO(rdev);
+       ret = PTR_ERR_OR_ZERO(info->rdev);
        if (ret) {
                dev_err(dev, "Failed to register VBUS regulator: %d\n", ret);
                return ret;