diff options
Diffstat (limited to 'drivers/power/charger-manager.c')
-rw-r--r-- | drivers/power/charger-manager.c | 310 |
1 files changed, 184 insertions, 126 deletions
diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index 6ba047f5ac2c..8acc3f8d303c 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c | |||
@@ -669,15 +669,21 @@ static void _setup_polling(struct work_struct *work) | |||
669 | WARN(cm_wq == NULL, "charger-manager: workqueue not initialized" | 669 | WARN(cm_wq == NULL, "charger-manager: workqueue not initialized" |
670 | ". try it later. %s\n", __func__); | 670 | ". try it later. %s\n", __func__); |
671 | 671 | ||
672 | /* | ||
673 | * Use mod_delayed_work() iff the next polling interval should | ||
674 | * occur before the currently scheduled one. If @cm_monitor_work | ||
675 | * isn't active, the end result is the same, so no need to worry | ||
676 | * about stale @next_polling. | ||
677 | */ | ||
672 | _next_polling = jiffies + polling_jiffy; | 678 | _next_polling = jiffies + polling_jiffy; |
673 | 679 | ||
674 | if (!delayed_work_pending(&cm_monitor_work) || | 680 | if (time_before(_next_polling, next_polling)) { |
675 | (delayed_work_pending(&cm_monitor_work) && | ||
676 | time_after(next_polling, _next_polling))) { | ||
677 | next_polling = jiffies + polling_jiffy; | ||
678 | mod_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy); | 681 | mod_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy); |
682 | next_polling = _next_polling; | ||
683 | } else { | ||
684 | if (queue_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy)) | ||
685 | next_polling = _next_polling; | ||
679 | } | 686 | } |
680 | |||
681 | out: | 687 | out: |
682 | mutex_unlock(&cm_list_mtx); | 688 | mutex_unlock(&cm_list_mtx); |
683 | } | 689 | } |
@@ -751,8 +757,7 @@ static void misc_event_handler(struct charger_manager *cm, | |||
751 | if (cm_suspended) | 757 | if (cm_suspended) |
752 | device_set_wakeup_capable(cm->dev, true); | 758 | device_set_wakeup_capable(cm->dev, true); |
753 | 759 | ||
754 | if (!delayed_work_pending(&cm_monitor_work) && | 760 | if (is_polling_required(cm) && cm->desc->polling_interval_ms) |
755 | is_polling_required(cm) && cm->desc->polling_interval_ms) | ||
756 | schedule_work(&setup_polling); | 761 | schedule_work(&setup_polling); |
757 | uevent_notify(cm, default_event_names[type]); | 762 | uevent_notify(cm, default_event_names[type]); |
758 | } | 763 | } |
@@ -1170,8 +1175,7 @@ static int charger_extcon_notifier(struct notifier_block *self, | |||
1170 | * when charger cable is attached. | 1175 | * when charger cable is attached. |
1171 | */ | 1176 | */ |
1172 | if (cable->attached && is_polling_required(cable->cm)) { | 1177 | if (cable->attached && is_polling_required(cable->cm)) { |
1173 | if (work_pending(&setup_polling)) | 1178 | cancel_work_sync(&setup_polling); |
1174 | cancel_work_sync(&setup_polling); | ||
1175 | schedule_work(&setup_polling); | 1179 | schedule_work(&setup_polling); |
1176 | } | 1180 | } |
1177 | 1181 | ||
@@ -1215,6 +1219,55 @@ static int charger_extcon_init(struct charger_manager *cm, | |||
1215 | return ret; | 1219 | return ret; |
1216 | } | 1220 | } |
1217 | 1221 | ||
1222 | /** | ||
1223 | * charger_manager_register_extcon - Register extcon device to recevie state | ||
1224 | * of charger cable. | ||
1225 | * @cm: the Charger Manager representing the battery. | ||
1226 | * | ||
1227 | * This function support EXTCON(External Connector) subsystem to detect the | ||
1228 | * state of charger cables for enabling or disabling charger(regulator) and | ||
1229 | * select the charger cable for charging among a number of external cable | ||
1230 | * according to policy of H/W board. | ||
1231 | */ | ||
1232 | static int charger_manager_register_extcon(struct charger_manager *cm) | ||
1233 | { | ||
1234 | struct charger_desc *desc = cm->desc; | ||
1235 | struct charger_regulator *charger; | ||
1236 | int ret = 0; | ||
1237 | int i; | ||
1238 | int j; | ||
1239 | |||
1240 | for (i = 0; i < desc->num_charger_regulators; i++) { | ||
1241 | charger = &desc->charger_regulators[i]; | ||
1242 | |||
1243 | charger->consumer = regulator_get(cm->dev, | ||
1244 | charger->regulator_name); | ||
1245 | if (charger->consumer == NULL) { | ||
1246 | dev_err(cm->dev, "Cannot find charger(%s)n", | ||
1247 | charger->regulator_name); | ||
1248 | ret = -EINVAL; | ||
1249 | goto err; | ||
1250 | } | ||
1251 | charger->cm = cm; | ||
1252 | |||
1253 | for (j = 0; j < charger->num_cables; j++) { | ||
1254 | struct charger_cable *cable = &charger->cables[j]; | ||
1255 | |||
1256 | ret = charger_extcon_init(cm, cable); | ||
1257 | if (ret < 0) { | ||
1258 | dev_err(cm->dev, "Cannot initialize charger(%s)n", | ||
1259 | charger->regulator_name); | ||
1260 | goto err; | ||
1261 | } | ||
1262 | cable->charger = charger; | ||
1263 | cable->cm = cm; | ||
1264 | } | ||
1265 | } | ||
1266 | |||
1267 | err: | ||
1268 | return ret; | ||
1269 | } | ||
1270 | |||
1218 | /* help function of sysfs node to control charger(regulator) */ | 1271 | /* help function of sysfs node to control charger(regulator) */ |
1219 | static ssize_t charger_name_show(struct device *dev, | 1272 | static ssize_t charger_name_show(struct device *dev, |
1220 | struct device_attribute *attr, char *buf) | 1273 | struct device_attribute *attr, char *buf) |
@@ -1274,7 +1327,7 @@ static ssize_t charger_externally_control_store(struct device *dev, | |||
1274 | 1327 | ||
1275 | for (i = 0; i < desc->num_charger_regulators; i++) { | 1328 | for (i = 0; i < desc->num_charger_regulators; i++) { |
1276 | if (&desc->charger_regulators[i] != charger && | 1329 | if (&desc->charger_regulators[i] != charger && |
1277 | !desc->charger_regulators[i].externally_control) { | 1330 | !desc->charger_regulators[i].externally_control) { |
1278 | /* | 1331 | /* |
1279 | * At least, one charger is controlled by | 1332 | * At least, one charger is controlled by |
1280 | * charger-manager | 1333 | * charger-manager |
@@ -1303,13 +1356,107 @@ static ssize_t charger_externally_control_store(struct device *dev, | |||
1303 | return count; | 1356 | return count; |
1304 | } | 1357 | } |
1305 | 1358 | ||
1359 | /** | ||
1360 | * charger_manager_register_sysfs - Register sysfs entry for each charger | ||
1361 | * @cm: the Charger Manager representing the battery. | ||
1362 | * | ||
1363 | * This function add sysfs entry for charger(regulator) to control charger from | ||
1364 | * user-space. If some development board use one more chargers for charging | ||
1365 | * but only need one charger on specific case which is dependent on user | ||
1366 | * scenario or hardware restrictions, the user enter 1 or 0(zero) to '/sys/ | ||
1367 | * class/power_supply/battery/charger.[index]/externally_control'. For example, | ||
1368 | * if user enter 1 to 'sys/class/power_supply/battery/charger.[index]/ | ||
1369 | * externally_control, this charger isn't controlled from charger-manager and | ||
1370 | * always stay off state of regulator. | ||
1371 | */ | ||
1372 | static int charger_manager_register_sysfs(struct charger_manager *cm) | ||
1373 | { | ||
1374 | struct charger_desc *desc = cm->desc; | ||
1375 | struct charger_regulator *charger; | ||
1376 | int chargers_externally_control = 1; | ||
1377 | char buf[11]; | ||
1378 | char *str; | ||
1379 | int ret = 0; | ||
1380 | int i; | ||
1381 | |||
1382 | /* Create sysfs entry to control charger(regulator) */ | ||
1383 | for (i = 0; i < desc->num_charger_regulators; i++) { | ||
1384 | charger = &desc->charger_regulators[i]; | ||
1385 | |||
1386 | snprintf(buf, 10, "charger.%d", i); | ||
1387 | str = kzalloc(sizeof(char) * (strlen(buf) + 1), GFP_KERNEL); | ||
1388 | if (!str) { | ||
1389 | dev_err(cm->dev, "Cannot allocate memory: %s\n", | ||
1390 | charger->regulator_name); | ||
1391 | ret = -ENOMEM; | ||
1392 | goto err; | ||
1393 | } | ||
1394 | strcpy(str, buf); | ||
1395 | |||
1396 | charger->attrs[0] = &charger->attr_name.attr; | ||
1397 | charger->attrs[1] = &charger->attr_state.attr; | ||
1398 | charger->attrs[2] = &charger->attr_externally_control.attr; | ||
1399 | charger->attrs[3] = NULL; | ||
1400 | charger->attr_g.name = str; | ||
1401 | charger->attr_g.attrs = charger->attrs; | ||
1402 | |||
1403 | sysfs_attr_init(&charger->attr_name.attr); | ||
1404 | charger->attr_name.attr.name = "name"; | ||
1405 | charger->attr_name.attr.mode = 0444; | ||
1406 | charger->attr_name.show = charger_name_show; | ||
1407 | |||
1408 | sysfs_attr_init(&charger->attr_state.attr); | ||
1409 | charger->attr_state.attr.name = "state"; | ||
1410 | charger->attr_state.attr.mode = 0444; | ||
1411 | charger->attr_state.show = charger_state_show; | ||
1412 | |||
1413 | sysfs_attr_init(&charger->attr_externally_control.attr); | ||
1414 | charger->attr_externally_control.attr.name | ||
1415 | = "externally_control"; | ||
1416 | charger->attr_externally_control.attr.mode = 0644; | ||
1417 | charger->attr_externally_control.show | ||
1418 | = charger_externally_control_show; | ||
1419 | charger->attr_externally_control.store | ||
1420 | = charger_externally_control_store; | ||
1421 | |||
1422 | if (!desc->charger_regulators[i].externally_control || | ||
1423 | !chargers_externally_control) | ||
1424 | chargers_externally_control = 0; | ||
1425 | |||
1426 | dev_info(cm->dev, "'%s' regulator's externally_control" | ||
1427 | "is %d\n", charger->regulator_name, | ||
1428 | charger->externally_control); | ||
1429 | |||
1430 | ret = sysfs_create_group(&cm->charger_psy.dev->kobj, | ||
1431 | &charger->attr_g); | ||
1432 | if (ret < 0) { | ||
1433 | dev_err(cm->dev, "Cannot create sysfs entry" | ||
1434 | "of %s regulator\n", | ||
1435 | charger->regulator_name); | ||
1436 | ret = -EINVAL; | ||
1437 | goto err; | ||
1438 | } | ||
1439 | } | ||
1440 | |||
1441 | if (chargers_externally_control) { | ||
1442 | dev_err(cm->dev, "Cannot register regulator because " | ||
1443 | "charger-manager must need at least " | ||
1444 | "one charger for charging battery\n"); | ||
1445 | |||
1446 | ret = -EINVAL; | ||
1447 | goto err; | ||
1448 | } | ||
1449 | |||
1450 | err: | ||
1451 | return ret; | ||
1452 | } | ||
1453 | |||
1306 | static int charger_manager_probe(struct platform_device *pdev) | 1454 | static int charger_manager_probe(struct platform_device *pdev) |
1307 | { | 1455 | { |
1308 | struct charger_desc *desc = dev_get_platdata(&pdev->dev); | 1456 | struct charger_desc *desc = dev_get_platdata(&pdev->dev); |
1309 | struct charger_manager *cm; | 1457 | struct charger_manager *cm; |
1310 | int ret = 0, i = 0; | 1458 | int ret = 0, i = 0; |
1311 | int j = 0; | 1459 | int j = 0; |
1312 | int chargers_externally_control = 1; | ||
1313 | union power_supply_propval val; | 1460 | union power_supply_propval val; |
1314 | 1461 | ||
1315 | if (g_desc && !rtc_dev && g_desc->rtc_name) { | 1462 | if (g_desc && !rtc_dev && g_desc->rtc_name) { |
@@ -1440,11 +1587,10 @@ static int charger_manager_probe(struct platform_device *pdev) | |||
1440 | 1587 | ||
1441 | memcpy(&cm->charger_psy, &psy_default, sizeof(psy_default)); | 1588 | memcpy(&cm->charger_psy, &psy_default, sizeof(psy_default)); |
1442 | 1589 | ||
1443 | if (!desc->psy_name) { | 1590 | if (!desc->psy_name) |
1444 | strncpy(cm->psy_name_buf, psy_default.name, PSY_NAME_MAX); | 1591 | strncpy(cm->psy_name_buf, psy_default.name, PSY_NAME_MAX); |
1445 | } else { | 1592 | else |
1446 | strncpy(cm->psy_name_buf, desc->psy_name, PSY_NAME_MAX); | 1593 | strncpy(cm->psy_name_buf, desc->psy_name, PSY_NAME_MAX); |
1447 | } | ||
1448 | cm->charger_psy.name = cm->psy_name_buf; | 1594 | cm->charger_psy.name = cm->psy_name_buf; |
1449 | 1595 | ||
1450 | /* Allocate for psy properties because they may vary */ | 1596 | /* Allocate for psy properties because they may vary */ |
@@ -1496,105 +1642,19 @@ static int charger_manager_probe(struct platform_device *pdev) | |||
1496 | goto err_register; | 1642 | goto err_register; |
1497 | } | 1643 | } |
1498 | 1644 | ||
1499 | for (i = 0 ; i < desc->num_charger_regulators ; i++) { | 1645 | /* Register extcon device for charger cable */ |
1500 | struct charger_regulator *charger | 1646 | ret = charger_manager_register_extcon(cm); |
1501 | = &desc->charger_regulators[i]; | 1647 | if (ret < 0) { |
1502 | char buf[11]; | 1648 | dev_err(&pdev->dev, "Cannot initialize extcon device\n"); |
1503 | char *str; | 1649 | goto err_reg_extcon; |
1504 | |||
1505 | charger->consumer = regulator_get(&pdev->dev, | ||
1506 | charger->regulator_name); | ||
1507 | if (charger->consumer == NULL) { | ||
1508 | dev_err(&pdev->dev, "Cannot find charger(%s)n", | ||
1509 | charger->regulator_name); | ||
1510 | ret = -EINVAL; | ||
1511 | goto err_chg_get; | ||
1512 | } | ||
1513 | charger->cm = cm; | ||
1514 | |||
1515 | for (j = 0 ; j < charger->num_cables ; j++) { | ||
1516 | struct charger_cable *cable = &charger->cables[j]; | ||
1517 | |||
1518 | ret = charger_extcon_init(cm, cable); | ||
1519 | if (ret < 0) { | ||
1520 | dev_err(&pdev->dev, "Cannot find charger(%s)n", | ||
1521 | charger->regulator_name); | ||
1522 | goto err_extcon; | ||
1523 | } | ||
1524 | cable->charger = charger; | ||
1525 | cable->cm = cm; | ||
1526 | } | ||
1527 | |||
1528 | /* Create sysfs entry to control charger(regulator) */ | ||
1529 | snprintf(buf, 10, "charger.%d", i); | ||
1530 | str = kzalloc(sizeof(char) * (strlen(buf) + 1), GFP_KERNEL); | ||
1531 | if (!str) { | ||
1532 | for (i--; i >= 0; i--) { | ||
1533 | charger = &desc->charger_regulators[i]; | ||
1534 | kfree(charger->attr_g.name); | ||
1535 | } | ||
1536 | ret = -ENOMEM; | ||
1537 | |||
1538 | goto err_extcon; | ||
1539 | } | ||
1540 | strcpy(str, buf); | ||
1541 | |||
1542 | charger->attrs[0] = &charger->attr_name.attr; | ||
1543 | charger->attrs[1] = &charger->attr_state.attr; | ||
1544 | charger->attrs[2] = &charger->attr_externally_control.attr; | ||
1545 | charger->attrs[3] = NULL; | ||
1546 | charger->attr_g.name = str; | ||
1547 | charger->attr_g.attrs = charger->attrs; | ||
1548 | |||
1549 | sysfs_attr_init(&charger->attr_name.attr); | ||
1550 | charger->attr_name.attr.name = "name"; | ||
1551 | charger->attr_name.attr.mode = 0444; | ||
1552 | charger->attr_name.show = charger_name_show; | ||
1553 | |||
1554 | sysfs_attr_init(&charger->attr_state.attr); | ||
1555 | charger->attr_state.attr.name = "state"; | ||
1556 | charger->attr_state.attr.mode = 0444; | ||
1557 | charger->attr_state.show = charger_state_show; | ||
1558 | |||
1559 | sysfs_attr_init(&charger->attr_externally_control.attr); | ||
1560 | charger->attr_externally_control.attr.name | ||
1561 | = "externally_control"; | ||
1562 | charger->attr_externally_control.attr.mode = 0644; | ||
1563 | charger->attr_externally_control.show | ||
1564 | = charger_externally_control_show; | ||
1565 | charger->attr_externally_control.store | ||
1566 | = charger_externally_control_store; | ||
1567 | |||
1568 | if (!desc->charger_regulators[i].externally_control || | ||
1569 | !chargers_externally_control) { | ||
1570 | chargers_externally_control = 0; | ||
1571 | } | ||
1572 | dev_info(&pdev->dev, "'%s' regulator's externally_control" | ||
1573 | "is %d\n", charger->regulator_name, | ||
1574 | charger->externally_control); | ||
1575 | |||
1576 | ret = sysfs_create_group(&cm->charger_psy.dev->kobj, | ||
1577 | &charger->attr_g); | ||
1578 | if (ret < 0) { | ||
1579 | dev_info(&pdev->dev, "Cannot create sysfs entry" | ||
1580 | "of %s regulator\n", | ||
1581 | charger->regulator_name); | ||
1582 | } | ||
1583 | } | ||
1584 | |||
1585 | if (chargers_externally_control) { | ||
1586 | dev_err(&pdev->dev, "Cannot register regulator because " | ||
1587 | "charger-manager must need at least " | ||
1588 | "one charger for charging battery\n"); | ||
1589 | |||
1590 | ret = -EINVAL; | ||
1591 | goto err_chg_enable; | ||
1592 | } | 1650 | } |
1593 | 1651 | ||
1594 | ret = try_charger_enable(cm, true); | 1652 | /* Register sysfs entry for charger(regulator) */ |
1595 | if (ret) { | 1653 | ret = charger_manager_register_sysfs(cm); |
1596 | dev_err(&pdev->dev, "Cannot enable charger regulators\n"); | 1654 | if (ret < 0) { |
1597 | goto err_chg_enable; | 1655 | dev_err(&pdev->dev, |
1656 | "Cannot initialize sysfs entry of regulator\n"); | ||
1657 | goto err_reg_sysfs; | ||
1598 | } | 1658 | } |
1599 | 1659 | ||
1600 | /* Add to the list */ | 1660 | /* Add to the list */ |
@@ -1613,27 +1673,28 @@ static int charger_manager_probe(struct platform_device *pdev) | |||
1613 | 1673 | ||
1614 | return 0; | 1674 | return 0; |
1615 | 1675 | ||
1616 | err_chg_enable: | 1676 | err_reg_sysfs: |
1617 | for (i = 0; i < desc->num_charger_regulators; i++) { | 1677 | for (i = 0; i < desc->num_charger_regulators; i++) { |
1618 | struct charger_regulator *charger; | 1678 | struct charger_regulator *charger; |
1619 | 1679 | ||
1620 | charger = &desc->charger_regulators[i]; | 1680 | charger = &desc->charger_regulators[i]; |
1621 | sysfs_remove_group(&cm->charger_psy.dev->kobj, | 1681 | sysfs_remove_group(&cm->charger_psy.dev->kobj, |
1622 | &charger->attr_g); | 1682 | &charger->attr_g); |
1683 | |||
1623 | kfree(charger->attr_g.name); | 1684 | kfree(charger->attr_g.name); |
1624 | } | 1685 | } |
1625 | err_extcon: | 1686 | err_reg_extcon: |
1626 | for (i = 0 ; i < desc->num_charger_regulators ; i++) { | 1687 | for (i = 0; i < desc->num_charger_regulators; i++) { |
1627 | struct charger_regulator *charger | 1688 | struct charger_regulator *charger; |
1628 | = &desc->charger_regulators[i]; | 1689 | |
1629 | for (j = 0 ; j < charger->num_cables ; j++) { | 1690 | charger = &desc->charger_regulators[i]; |
1691 | for (j = 0; j < charger->num_cables; j++) { | ||
1630 | struct charger_cable *cable = &charger->cables[j]; | 1692 | struct charger_cable *cable = &charger->cables[j]; |
1631 | extcon_unregister_interest(&cable->extcon_dev); | 1693 | extcon_unregister_interest(&cable->extcon_dev); |
1632 | } | 1694 | } |
1633 | } | 1695 | |
1634 | err_chg_get: | ||
1635 | for (i = 0 ; i < desc->num_charger_regulators ; i++) | ||
1636 | regulator_put(desc->charger_regulators[i].consumer); | 1696 | regulator_put(desc->charger_regulators[i].consumer); |
1697 | } | ||
1637 | 1698 | ||
1638 | power_supply_unregister(&cm->charger_psy); | 1699 | power_supply_unregister(&cm->charger_psy); |
1639 | err_register: | 1700 | err_register: |
@@ -1661,10 +1722,8 @@ static int charger_manager_remove(struct platform_device *pdev) | |||
1661 | list_del(&cm->entry); | 1722 | list_del(&cm->entry); |
1662 | mutex_unlock(&cm_list_mtx); | 1723 | mutex_unlock(&cm_list_mtx); |
1663 | 1724 | ||
1664 | if (work_pending(&setup_polling)) | 1725 | cancel_work_sync(&setup_polling); |
1665 | cancel_work_sync(&setup_polling); | 1726 | cancel_delayed_work_sync(&cm_monitor_work); |
1666 | if (delayed_work_pending(&cm_monitor_work)) | ||
1667 | cancel_delayed_work_sync(&cm_monitor_work); | ||
1668 | 1727 | ||
1669 | for (i = 0 ; i < desc->num_charger_regulators ; i++) { | 1728 | for (i = 0 ; i < desc->num_charger_regulators ; i++) { |
1670 | struct charger_regulator *charger | 1729 | struct charger_regulator *charger |
@@ -1733,8 +1792,7 @@ static int cm_suspend_prepare(struct device *dev) | |||
1733 | cm_suspended = true; | 1792 | cm_suspended = true; |
1734 | } | 1793 | } |
1735 | 1794 | ||
1736 | if (delayed_work_pending(&cm->fullbatt_vchk_work)) | 1795 | cancel_delayed_work(&cm->fullbatt_vchk_work); |
1737 | cancel_delayed_work(&cm->fullbatt_vchk_work); | ||
1738 | cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm); | 1796 | cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm); |
1739 | cm->status_save_batt = is_batt_present(cm); | 1797 | cm->status_save_batt = is_batt_present(cm); |
1740 | 1798 | ||