]> Pileus Git - ~andy/linux/blobdiff - drivers/power/charger-manager.c
Merge tag 'for-v3.7' of git://git.infradead.org/battery-2.6
[~andy/linux] / drivers / power / charger-manager.c
index e92ec55ced91723abfdf7f112b1b6eb259ef626f..8a0aca6364c7e8ba9ab68261b598afe2e155ad18 100644 (file)
@@ -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,
@@ -673,9 +680,8 @@ static void _setup_polling(struct work_struct *work)
        if (!delayed_work_pending(&cm_monitor_work) ||
            (delayed_work_pending(&cm_monitor_work) &&
             time_after(next_polling, _next_polling))) {
-               cancel_delayed_work_sync(&cm_monitor_work);
                next_polling = jiffies + polling_jiffy;
-               queue_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy);
+               mod_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy);
        }
 
 out:
@@ -710,10 +716,8 @@ static void fullbatt_handler(struct charger_manager *cm)
        if (cm_suspended)
                device_set_wakeup_capable(cm->dev, true);
 
-       if (delayed_work_pending(&cm->fullbatt_vchk_work))
-               cancel_delayed_work(&cm->fullbatt_vchk_work);
-       queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
-                          msecs_to_jiffies(desc->fullbatt_vchkdrop_ms));
+       mod_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
+                        msecs_to_jiffies(desc->fullbatt_vchkdrop_ms));
        cm->fullbatt_vchk_jiffies_at = jiffies + msecs_to_jiffies(
                                       desc->fullbatt_vchkdrop_ms);
 
@@ -1217,12 +1221,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 +1505,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 +1516,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 +1530,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 +1620,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