diff options
author | Chanwoo Choi <cw00.choi@samsung.com> | 2012-09-21 18:49:37 +0900 |
---|---|---|
committer | Anton Vorontsov <anton.vorontsov@linaro.org> | 2012-09-21 16:32:40 -0700 |
commit | 3950c7865cd7c963982a2c94457182b96732f4c9 (patch) | |
tree | e5fdab9c3c128362f3ac423d8275dc7b12e7439b /drivers/power | |
parent | 8fcfe088e21aa0db9d62eaf565757def673efba6 (diff) | |
download | linux-stable-3950c7865cd7c963982a2c94457182b96732f4c9.tar.gz linux-stable-3950c7865cd7c963982a2c94457182b96732f4c9.tar.bz2 linux-stable-3950c7865cd7c963982a2c94457182b96732f4c9.zip |
charger-manager: Add support sysfs entry for charger
This patch add support sysfs entry for each charger(regulator).
Charger-manager use one or more chargers for charging battery but some
charger isn't necessary on specific scenario. So, if some charger isn't
needed, can disable specific charger through 'externally_control' entry
while system is on state and confirm the information(name, state) of
charger.
The list of added sysfs entry
- /sys/class/power_supply/battery/chargers/charger.[index]/name
show name of charger(regulator)
- /sys/class/power_supply/battery/chargers/charger.[index]/state
show either enabled or disabled state of charger
- /sys/class/power_supply/battery/chargers/charger.[index]/externally_control
If 'externally_control' of specific charger is 1, Charger-manager cannot
enable regulator for charging when charger cable is attached and charger
must be maintained with disabled state. If 'externally_control' is zero,
Charger-manager usually can control to enable/disable regulator.
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: Myungjoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Anton Vorontsov <anton.vorontsov@linaro.org>
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/charger-manager.c | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index e92ec55ced91..92dfa5c64876 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -22,6 +22,7 @@ #include <linux/platform_device.h> #include <linux/power/charger-manager.h> #include <linux/regulator/consumer.h> +#include <linux/sysfs.h> static const char * const default_event_names[] = { [CM_EVENT_UNKNOWN] = "Unknown", @@ -332,6 +333,9 @@ static int try_charger_enable(struct charger_manager *cm, bool enable) cm->charging_end_time = 0; for (i = 0 ; i < desc->num_charger_regulators ; i++) { + if (desc->charger_regulators[i].externally_control) + continue; + err = regulator_enable(desc->charger_regulators[i].consumer); if (err < 0) { dev_warn(cm->dev, @@ -348,6 +352,9 @@ static int try_charger_enable(struct charger_manager *cm, bool enable) cm->charging_end_time = ktime_to_ms(ktime_get()); for (i = 0 ; i < desc->num_charger_regulators ; i++) { + if (desc->charger_regulators[i].externally_control) + continue; + err = regulator_disable(desc->charger_regulators[i].consumer); if (err < 0) { dev_warn(cm->dev, @@ -1217,12 +1224,101 @@ static int charger_extcon_init(struct charger_manager *cm, return ret; } +/* help function of sysfs node to control charger(regulator) */ +static ssize_t charger_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct charger_regulator *charger + = container_of(attr, struct charger_regulator, attr_name); + + return sprintf(buf, "%s\n", charger->regulator_name); +} + +static ssize_t charger_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct charger_regulator *charger + = container_of(attr, struct charger_regulator, attr_state); + int state = 0; + + if (!charger->externally_control) + state = regulator_is_enabled(charger->consumer); + + return sprintf(buf, "%s\n", state ? "enabled" : "disabled"); +} + +static ssize_t charger_externally_control_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct charger_regulator *charger = container_of(attr, + struct charger_regulator, attr_externally_control); + + return sprintf(buf, "%d\n", charger->externally_control); +} + +static ssize_t charger_externally_control_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct charger_regulator *charger + = container_of(attr, struct charger_regulator, + attr_externally_control); + struct charger_manager *cm = charger->cm; + struct charger_desc *desc = cm->desc; + int i; + int ret; + int externally_control; + int chargers_externally_control = 1; + + ret = sscanf(buf, "%d", &externally_control); + if (ret == 0) { + ret = -EINVAL; + return ret; + } + + if (!externally_control) { + charger->externally_control = 0; + return count; + } + + for (i = 0; i < desc->num_charger_regulators; i++) { + if (&desc->charger_regulators[i] != charger && + !desc->charger_regulators[i].externally_control) { + /* + * At least, one charger is controlled by + * charger-manager + */ + chargers_externally_control = 0; + break; + } + } + + if (!chargers_externally_control) { + if (cm->charger_enabled) { + try_charger_enable(charger->cm, false); + charger->externally_control = externally_control; + try_charger_enable(charger->cm, true); + } else { + charger->externally_control = externally_control; + } + } else { + dev_warn(cm->dev, + "'%s' regulator should be controlled " + "in charger-manager because charger-manager " + "must need at least one charger for charging\n", + charger->regulator_name); + } + + return count; +} + static int charger_manager_probe(struct platform_device *pdev) { struct charger_desc *desc = dev_get_platdata(&pdev->dev); struct charger_manager *cm; int ret = 0, i = 0; int j = 0; + int chargers_externally_control = 1; union power_supply_propval val; if (g_desc && !rtc_dev && g_desc->rtc_name) { @@ -1412,6 +1508,8 @@ static int charger_manager_probe(struct platform_device *pdev) for (i = 0 ; i < desc->num_charger_regulators ; i++) { struct charger_regulator *charger = &desc->charger_regulators[i]; + char buf[11]; + char *str; charger->consumer = regulator_get(&pdev->dev, charger->regulator_name); @@ -1421,6 +1519,7 @@ static int charger_manager_probe(struct platform_device *pdev) ret = -EINVAL; goto err_chg_get; } + charger->cm = cm; for (j = 0 ; j < charger->num_cables ; j++) { struct charger_cable *cable = &charger->cables[j]; @@ -1434,6 +1533,71 @@ static int charger_manager_probe(struct platform_device *pdev) cable->charger = charger; cable->cm = cm; } + + /* Create sysfs entry to control charger(regulator) */ + snprintf(buf, 10, "charger.%d", i); + str = kzalloc(sizeof(char) * (strlen(buf) + 1), GFP_KERNEL); + if (!str) { + for (i--; i >= 0; i--) { + charger = &desc->charger_regulators[i]; + kfree(charger->attr_g.name); + } + ret = -ENOMEM; + + goto err_extcon; + } + strcpy(str, buf); + + charger->attrs[0] = &charger->attr_name.attr; + charger->attrs[1] = &charger->attr_state.attr; + charger->attrs[2] = &charger->attr_externally_control.attr; + charger->attrs[3] = NULL; + charger->attr_g.name = str; + charger->attr_g.attrs = charger->attrs; + + sysfs_attr_init(&charger->attr_name.attr); + charger->attr_name.attr.name = "name"; + charger->attr_name.attr.mode = 0444; + charger->attr_name.show = charger_name_show; + + sysfs_attr_init(&charger->attr_state.attr); + charger->attr_state.attr.name = "state"; + charger->attr_state.attr.mode = 0444; + charger->attr_state.show = charger_state_show; + + sysfs_attr_init(&charger->attr_externally_control.attr); + charger->attr_externally_control.attr.name + = "externally_control"; + charger->attr_externally_control.attr.mode = 0644; + charger->attr_externally_control.show + = charger_externally_control_show; + charger->attr_externally_control.store + = charger_externally_control_store; + + if (!desc->charger_regulators[i].externally_control || + !chargers_externally_control) { + chargers_externally_control = 0; + } + dev_info(&pdev->dev, "'%s' regulator's externally_control" + "is %d\n", charger->regulator_name, + charger->externally_control); + + ret = sysfs_create_group(&cm->charger_psy.dev->kobj, + &charger->attr_g); + if (ret < 0) { + dev_info(&pdev->dev, "Cannot create sysfs entry" + "of %s regulator\n", + charger->regulator_name); + } + } + + if (chargers_externally_control) { + dev_err(&pdev->dev, "Cannot register regulator because " + "charger-manager must need at least " + "one charger for charging battery\n"); + + ret = -EINVAL; + goto err_chg_enable; } ret = try_charger_enable(cm, true); @@ -1459,6 +1623,14 @@ static int charger_manager_probe(struct platform_device *pdev) return 0; err_chg_enable: + for (i = 0; i < desc->num_charger_regulators; i++) { + struct charger_regulator *charger; + + charger = &desc->charger_regulators[i]; + sysfs_remove_group(&cm->charger_psy.dev->kobj, + &charger->attr_g); + kfree(charger->attr_g.name); + } err_extcon: for (i = 0 ; i < desc->num_charger_regulators ; i++) { struct charger_regulator *charger |