summaryrefslogtreecommitdiff
path: root/drivers/power/supply/ucs1002_power.c
diff options
context:
space:
mode:
authorLucas Stach <l.stach@pengutronix.de>2019-11-19 20:00:04 +0300
committerSebastian Reichel <sre@kernel.org>2019-12-19 03:07:53 +0300
commita3d70dacc727ada212aecb8d131a3910622763d6 (patch)
treee0af520b3031f90e7189a5516caa17b6c9b4fee3 /drivers/power/supply/ucs1002_power.c
parent3c9c2d08128a510f67c5443387af5e2176407596 (diff)
downloadlinux-a3d70dacc727ada212aecb8d131a3910622763d6.tar.xz
power: suppy: ucs1002: disable power when max current is 0
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>
Diffstat (limited to 'drivers/power/supply/ucs1002_power.c')
-rw-r--r--drivers/power/supply/ucs1002_power.c42
1 files changed, 38 insertions, 4 deletions
diff --git a/drivers/power/supply/ucs1002_power.c b/drivers/power/supply/ucs1002_power.c
index 1b80ae479e7d..0ca80d00b80a 100644
--- a/drivers/power/supply/ucs1002_power.c
+++ b/drivers/power/supply/ucs1002_power.c
@@ -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;