mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-12 05:48:40 +00:00
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:
parent
3c9c2d0812
commit
a3d70dacc7
1 changed files with 38 additions and 4 deletions
|
@ -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, ®);
|
ret = regmap_read(info->regmap, UCS1002_REG_ILIMIT, ®);
|
||||||
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,
|
||||||
®ulator_config);
|
®ulator_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;
|
||||||
|
|
Loading…
Reference in a new issue