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>
This commit is contained in:
Lucas Stach 2019-11-19 18:00:04 +01:00 committed by Sebastian Reichel
parent 3c9c2d0812
commit a3d70dacc7

View File

@ -100,7 +100,9 @@ struct ucs1002_info {
struct i2c_client *client; struct i2c_client *client;
struct regmap *regmap; struct regmap *regmap;
struct regulator_desc *regulator_descriptor; struct regulator_desc *regulator_descriptor;
struct regulator_dev *rdev;
bool present; bool present;
bool output_disable;
}; };
static enum power_supply_property ucs1002_props[] = { static enum power_supply_property ucs1002_props[] = {
@ -233,6 +235,11 @@ static int ucs1002_get_max_current(struct ucs1002_info *info,
unsigned int reg; unsigned int reg;
int ret; int ret;
if (info->output_disable) {
val->intval = 0;
return 0;
}
ret = regmap_read(info->regmap, UCS1002_REG_ILIMIT, &reg); ret = regmap_read(info->regmap, UCS1002_REG_ILIMIT, &reg);
if (ret) if (ret)
return ret; return ret;
@ -247,6 +254,12 @@ static int ucs1002_set_max_current(struct ucs1002_info *info, u32 val)
unsigned int reg; unsigned int reg;
int ret, idx; 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++) { for (idx = 0; idx < ARRAY_SIZE(ucs1002_current_limit_uA); idx++) {
if (val == ucs1002_current_limit_uA[idx]) if (val == ucs1002_current_limit_uA[idx])
break; break;
@ -270,6 +283,12 @@ static int ucs1002_set_max_current(struct ucs1002_info *info, u32 val)
if (reg != idx) if (reg != idx)
return -EINVAL; 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; return 0;
} }
@ -470,9 +489,24 @@ static irqreturn_t ucs1002_alert_irq(int irq, void *data)
return IRQ_HANDLED; 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 = { static const struct regulator_ops ucs1002_regulator_ops = {
.is_enabled = regulator_is_enabled_regmap, .is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap, .enable = ucs1002_regulator_enable,
.disable = regulator_disable_regmap, .disable = regulator_disable_regmap,
}; };
@ -499,7 +533,6 @@ static int ucs1002_probe(struct i2c_client *client,
}; };
struct regulator_config regulator_config = {}; struct regulator_config regulator_config = {};
int irq_a_det, irq_alert, ret; int irq_a_det, irq_alert, ret;
struct regulator_dev *rdev;
struct ucs1002_info *info; struct ucs1002_info *info;
unsigned int regval; unsigned int regval;
@ -589,10 +622,11 @@ static int ucs1002_probe(struct i2c_client *client,
regulator_config.dev = dev; regulator_config.dev = dev;
regulator_config.of_node = dev->of_node; regulator_config.of_node = dev->of_node;
regulator_config.regmap = info->regmap; 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); &regulator_config);
ret = PTR_ERR_OR_ZERO(rdev); ret = PTR_ERR_OR_ZERO(info->rdev);
if (ret) { if (ret) {
dev_err(dev, "Failed to register VBUS regulator: %d\n", ret); dev_err(dev, "Failed to register VBUS regulator: %d\n", ret);
return ret; return ret;