diff options
author | Donggeun Kim <dg77.kim@samsung.com> | 2011-12-27 04:47:49 -0500 |
---|---|---|
committer | Anton Vorontsov <cbouatmailru@gmail.com> | 2012-01-03 23:08:45 -0500 |
commit | ad3d13eee78ec44194bf919a37e2f711e53cbdf0 (patch) | |
tree | e7a950b1e1d2642c4fae0a7019ac7ceed5241104 /drivers/power | |
parent | 3bb3dbbd56ea39e5537db8f8041ea95d28f16a7f (diff) |
power_supply: Charger-Manager: Add properties for power-supply-class
Charger Manager provides power-supply-class aggregating
information from multiple chargers and a fuel-gauge.
Signed-off-by: Donggeun Kim <dg77.kim@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 <cbouatmailru@gmail.com>
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/charger-manager.c | 295 |
1 files changed, 294 insertions, 1 deletions
diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index 727a259ea46c..0378d019efae 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c | |||
@@ -122,6 +122,32 @@ static bool is_ext_pwr_online(struct charger_manager *cm) | |||
122 | } | 122 | } |
123 | 123 | ||
124 | /** | 124 | /** |
125 | * get_batt_uV - Get the voltage level of the battery | ||
126 | * @cm: the Charger Manager representing the battery. | ||
127 | * @uV: the voltage level returned. | ||
128 | * | ||
129 | * Returns 0 if there is no error. | ||
130 | * Returns a negative value on error. | ||
131 | */ | ||
132 | static int get_batt_uV(struct charger_manager *cm, int *uV) | ||
133 | { | ||
134 | union power_supply_propval val; | ||
135 | int ret; | ||
136 | |||
137 | if (cm->fuel_gauge) | ||
138 | ret = cm->fuel_gauge->get_property(cm->fuel_gauge, | ||
139 | POWER_SUPPLY_PROP_VOLTAGE_NOW, &val); | ||
140 | else | ||
141 | return -ENODEV; | ||
142 | |||
143 | if (ret) | ||
144 | return ret; | ||
145 | |||
146 | *uV = val.intval; | ||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | /** | ||
125 | * is_charging - Returns true if the battery is being charged. | 151 | * is_charging - Returns true if the battery is being charged. |
126 | * @cm: the Charger Manager representing the battery. | 152 | * @cm: the Charger Manager representing the battery. |
127 | */ | 153 | */ |
@@ -369,6 +395,208 @@ static bool cm_monitor(void) | |||
369 | return stop; | 395 | return stop; |
370 | } | 396 | } |
371 | 397 | ||
398 | static int charger_get_property(struct power_supply *psy, | ||
399 | enum power_supply_property psp, | ||
400 | union power_supply_propval *val) | ||
401 | { | ||
402 | struct charger_manager *cm = container_of(psy, | ||
403 | struct charger_manager, charger_psy); | ||
404 | struct charger_desc *desc = cm->desc; | ||
405 | int i, ret = 0, uV; | ||
406 | |||
407 | switch (psp) { | ||
408 | case POWER_SUPPLY_PROP_STATUS: | ||
409 | if (is_charging(cm)) | ||
410 | val->intval = POWER_SUPPLY_STATUS_CHARGING; | ||
411 | else if (is_ext_pwr_online(cm)) | ||
412 | val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; | ||
413 | else | ||
414 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; | ||
415 | break; | ||
416 | case POWER_SUPPLY_PROP_HEALTH: | ||
417 | if (cm->emergency_stop > 0) | ||
418 | val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; | ||
419 | else if (cm->emergency_stop < 0) | ||
420 | val->intval = POWER_SUPPLY_HEALTH_COLD; | ||
421 | else | ||
422 | val->intval = POWER_SUPPLY_HEALTH_GOOD; | ||
423 | break; | ||
424 | case POWER_SUPPLY_PROP_PRESENT: | ||
425 | if (is_batt_present(cm)) | ||
426 | val->intval = 1; | ||
427 | else | ||
428 | val->intval = 0; | ||
429 | break; | ||
430 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
431 | ret = get_batt_uV(cm, &i); | ||
432 | val->intval = i; | ||
433 | break; | ||
434 | case POWER_SUPPLY_PROP_CURRENT_NOW: | ||
435 | ret = cm->fuel_gauge->get_property(cm->fuel_gauge, | ||
436 | POWER_SUPPLY_PROP_CURRENT_NOW, val); | ||
437 | break; | ||
438 | case POWER_SUPPLY_PROP_TEMP: | ||
439 | /* in thenth of centigrade */ | ||
440 | if (cm->last_temp_mC == INT_MIN) | ||
441 | desc->temperature_out_of_range(&cm->last_temp_mC); | ||
442 | val->intval = cm->last_temp_mC / 100; | ||
443 | if (!desc->measure_battery_temp) | ||
444 | ret = -ENODEV; | ||
445 | break; | ||
446 | case POWER_SUPPLY_PROP_TEMP_AMBIENT: | ||
447 | /* in thenth of centigrade */ | ||
448 | if (cm->last_temp_mC == INT_MIN) | ||
449 | desc->temperature_out_of_range(&cm->last_temp_mC); | ||
450 | val->intval = cm->last_temp_mC / 100; | ||
451 | if (desc->measure_battery_temp) | ||
452 | ret = -ENODEV; | ||
453 | break; | ||
454 | case POWER_SUPPLY_PROP_CAPACITY: | ||
455 | if (!cm->fuel_gauge) { | ||
456 | ret = -ENODEV; | ||
457 | break; | ||
458 | } | ||
459 | |||
460 | if (!is_batt_present(cm)) { | ||
461 | /* There is no battery. Assume 100% */ | ||
462 | val->intval = 100; | ||
463 | break; | ||
464 | } | ||
465 | |||
466 | ret = cm->fuel_gauge->get_property(cm->fuel_gauge, | ||
467 | POWER_SUPPLY_PROP_CAPACITY, val); | ||
468 | if (ret) | ||
469 | break; | ||
470 | |||
471 | if (val->intval > 100) { | ||
472 | val->intval = 100; | ||
473 | break; | ||
474 | } | ||
475 | if (val->intval < 0) | ||
476 | val->intval = 0; | ||
477 | |||
478 | /* Do not adjust SOC when charging: voltage is overrated */ | ||
479 | if (is_charging(cm)) | ||
480 | break; | ||
481 | |||
482 | /* | ||
483 | * If the capacity value is inconsistent, calibrate it base on | ||
484 | * the battery voltage values and the thresholds given as desc | ||
485 | */ | ||
486 | ret = get_batt_uV(cm, &uV); | ||
487 | if (ret) { | ||
488 | /* Voltage information not available. No calibration */ | ||
489 | ret = 0; | ||
490 | break; | ||
491 | } | ||
492 | |||
493 | if (desc->fullbatt_uV > 0 && uV >= desc->fullbatt_uV && | ||
494 | !is_charging(cm)) { | ||
495 | val->intval = 100; | ||
496 | break; | ||
497 | } | ||
498 | |||
499 | break; | ||
500 | case POWER_SUPPLY_PROP_ONLINE: | ||
501 | if (is_ext_pwr_online(cm)) | ||
502 | val->intval = 1; | ||
503 | else | ||
504 | val->intval = 0; | ||
505 | break; | ||
506 | case POWER_SUPPLY_PROP_CHARGE_FULL: | ||
507 | if (cm->fuel_gauge) { | ||
508 | if (cm->fuel_gauge->get_property(cm->fuel_gauge, | ||
509 | POWER_SUPPLY_PROP_CHARGE_FULL, val) == 0) | ||
510 | break; | ||
511 | } | ||
512 | |||
513 | if (is_ext_pwr_online(cm)) { | ||
514 | /* Not full if it's charging. */ | ||
515 | if (is_charging(cm)) { | ||
516 | val->intval = 0; | ||
517 | break; | ||
518 | } | ||
519 | /* | ||
520 | * Full if it's powered but not charging andi | ||
521 | * not forced stop by emergency | ||
522 | */ | ||
523 | if (!cm->emergency_stop) { | ||
524 | val->intval = 1; | ||
525 | break; | ||
526 | } | ||
527 | } | ||
528 | |||
529 | /* Full if it's over the fullbatt voltage */ | ||
530 | ret = get_batt_uV(cm, &uV); | ||
531 | if (!ret && desc->fullbatt_uV > 0 && uV >= desc->fullbatt_uV && | ||
532 | !is_charging(cm)) { | ||
533 | val->intval = 1; | ||
534 | break; | ||
535 | } | ||
536 | |||
537 | /* Full if the cap is 100 */ | ||
538 | if (cm->fuel_gauge) { | ||
539 | ret = cm->fuel_gauge->get_property(cm->fuel_gauge, | ||
540 | POWER_SUPPLY_PROP_CAPACITY, val); | ||
541 | if (!ret && val->intval >= 100 && !is_charging(cm)) { | ||
542 | val->intval = 1; | ||
543 | break; | ||
544 | } | ||
545 | } | ||
546 | |||
547 | val->intval = 0; | ||
548 | ret = 0; | ||
549 | break; | ||
550 | case POWER_SUPPLY_PROP_CHARGE_NOW: | ||
551 | if (is_charging(cm)) { | ||
552 | ret = cm->fuel_gauge->get_property(cm->fuel_gauge, | ||
553 | POWER_SUPPLY_PROP_CHARGE_NOW, | ||
554 | val); | ||
555 | if (ret) { | ||
556 | val->intval = 1; | ||
557 | ret = 0; | ||
558 | } else { | ||
559 | /* If CHARGE_NOW is supplied, use it */ | ||
560 | val->intval = (val->intval > 0) ? | ||
561 | val->intval : 1; | ||
562 | } | ||
563 | } else { | ||
564 | val->intval = 0; | ||
565 | } | ||
566 | break; | ||
567 | default: | ||
568 | return -EINVAL; | ||
569 | } | ||
570 | return ret; | ||
571 | } | ||
572 | |||
573 | #define NUM_CHARGER_PSY_OPTIONAL (4) | ||
574 | static enum power_supply_property default_charger_props[] = { | ||
575 | /* Guaranteed to provide */ | ||
576 | POWER_SUPPLY_PROP_STATUS, | ||
577 | POWER_SUPPLY_PROP_HEALTH, | ||
578 | POWER_SUPPLY_PROP_PRESENT, | ||
579 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
580 | POWER_SUPPLY_PROP_CAPACITY, | ||
581 | POWER_SUPPLY_PROP_ONLINE, | ||
582 | POWER_SUPPLY_PROP_CHARGE_FULL, | ||
583 | /* | ||
584 | * Optional properties are: | ||
585 | * POWER_SUPPLY_PROP_CHARGE_NOW, | ||
586 | * POWER_SUPPLY_PROP_CURRENT_NOW, | ||
587 | * POWER_SUPPLY_PROP_TEMP, and | ||
588 | * POWER_SUPPLY_PROP_TEMP_AMBIENT, | ||
589 | */ | ||
590 | }; | ||
591 | |||
592 | static struct power_supply psy_default = { | ||
593 | .name = "battery", | ||
594 | .type = POWER_SUPPLY_TYPE_BATTERY, | ||
595 | .properties = default_charger_props, | ||
596 | .num_properties = ARRAY_SIZE(default_charger_props), | ||
597 | .get_property = charger_get_property, | ||
598 | }; | ||
599 | |||
372 | /** | 600 | /** |
373 | * cm_setup_timer - For in-suspend monitoring setup wakeup alarm | 601 | * cm_setup_timer - For in-suspend monitoring setup wakeup alarm |
374 | * for suspend_again. | 602 | * for suspend_again. |
@@ -532,6 +760,7 @@ static int charger_manager_probe(struct platform_device *pdev) | |||
532 | struct charger_desc *desc = dev_get_platdata(&pdev->dev); | 760 | struct charger_desc *desc = dev_get_platdata(&pdev->dev); |
533 | struct charger_manager *cm; | 761 | struct charger_manager *cm; |
534 | int ret = 0, i = 0; | 762 | int ret = 0, i = 0; |
763 | union power_supply_propval val; | ||
535 | 764 | ||
536 | if (g_desc && !rtc_dev && g_desc->rtc_name) { | 765 | if (g_desc && !rtc_dev && g_desc->rtc_name) { |
537 | rtc_dev = rtc_class_open(g_desc->rtc_name); | 766 | rtc_dev = rtc_class_open(g_desc->rtc_name); |
@@ -626,11 +855,68 @@ static int charger_manager_probe(struct platform_device *pdev) | |||
626 | 855 | ||
627 | platform_set_drvdata(pdev, cm); | 856 | platform_set_drvdata(pdev, cm); |
628 | 857 | ||
858 | memcpy(&cm->charger_psy, &psy_default, | ||
859 | sizeof(psy_default)); | ||
860 | if (!desc->psy_name) { | ||
861 | strncpy(cm->psy_name_buf, psy_default.name, | ||
862 | PSY_NAME_MAX); | ||
863 | } else { | ||
864 | strncpy(cm->psy_name_buf, desc->psy_name, PSY_NAME_MAX); | ||
865 | } | ||
866 | cm->charger_psy.name = cm->psy_name_buf; | ||
867 | |||
868 | /* Allocate for psy properties because they may vary */ | ||
869 | cm->charger_psy.properties = kzalloc(sizeof(enum power_supply_property) | ||
870 | * (ARRAY_SIZE(default_charger_props) + | ||
871 | NUM_CHARGER_PSY_OPTIONAL), | ||
872 | GFP_KERNEL); | ||
873 | if (!cm->charger_psy.properties) { | ||
874 | dev_err(&pdev->dev, "Cannot allocate for psy properties.\n"); | ||
875 | ret = -ENOMEM; | ||
876 | goto err_chg_stat; | ||
877 | } | ||
878 | memcpy(cm->charger_psy.properties, default_charger_props, | ||
879 | sizeof(enum power_supply_property) * | ||
880 | ARRAY_SIZE(default_charger_props)); | ||
881 | cm->charger_psy.num_properties = psy_default.num_properties; | ||
882 | |||
883 | /* Find which optional psy-properties are available */ | ||
884 | if (!cm->fuel_gauge->get_property(cm->fuel_gauge, | ||
885 | POWER_SUPPLY_PROP_CHARGE_NOW, &val)) { | ||
886 | cm->charger_psy.properties[cm->charger_psy.num_properties] = | ||
887 | POWER_SUPPLY_PROP_CHARGE_NOW; | ||
888 | cm->charger_psy.num_properties++; | ||
889 | } | ||
890 | if (!cm->fuel_gauge->get_property(cm->fuel_gauge, | ||
891 | POWER_SUPPLY_PROP_CURRENT_NOW, | ||
892 | &val)) { | ||
893 | cm->charger_psy.properties[cm->charger_psy.num_properties] = | ||
894 | POWER_SUPPLY_PROP_CURRENT_NOW; | ||
895 | cm->charger_psy.num_properties++; | ||
896 | } | ||
897 | if (!desc->measure_battery_temp) { | ||
898 | cm->charger_psy.properties[cm->charger_psy.num_properties] = | ||
899 | POWER_SUPPLY_PROP_TEMP_AMBIENT; | ||
900 | cm->charger_psy.num_properties++; | ||
901 | } | ||
902 | if (desc->measure_battery_temp) { | ||
903 | cm->charger_psy.properties[cm->charger_psy.num_properties] = | ||
904 | POWER_SUPPLY_PROP_TEMP; | ||
905 | cm->charger_psy.num_properties++; | ||
906 | } | ||
907 | |||
908 | ret = power_supply_register(NULL, &cm->charger_psy); | ||
909 | if (ret) { | ||
910 | dev_err(&pdev->dev, "Cannot register charger-manager with" | ||
911 | " name \"%s\".\n", cm->charger_psy.name); | ||
912 | goto err_register; | ||
913 | } | ||
914 | |||
629 | ret = regulator_bulk_get(&pdev->dev, desc->num_charger_regulators, | 915 | ret = regulator_bulk_get(&pdev->dev, desc->num_charger_regulators, |
630 | desc->charger_regulators); | 916 | desc->charger_regulators); |
631 | if (ret) { | 917 | if (ret) { |
632 | dev_err(&pdev->dev, "Cannot get charger regulators.\n"); | 918 | dev_err(&pdev->dev, "Cannot get charger regulators.\n"); |
633 | goto err_chg_stat; | 919 | goto err_bulk_get; |
634 | } | 920 | } |
635 | 921 | ||
636 | ret = try_charger_enable(cm, true); | 922 | ret = try_charger_enable(cm, true); |
@@ -650,6 +936,10 @@ err_chg_enable: | |||
650 | if (desc->charger_regulators) | 936 | if (desc->charger_regulators) |
651 | regulator_bulk_free(desc->num_charger_regulators, | 937 | regulator_bulk_free(desc->num_charger_regulators, |
652 | desc->charger_regulators); | 938 | desc->charger_regulators); |
939 | err_bulk_get: | ||
940 | power_supply_unregister(&cm->charger_psy); | ||
941 | err_register: | ||
942 | kfree(cm->charger_psy.properties); | ||
653 | err_chg_stat: | 943 | err_chg_stat: |
654 | kfree(cm->charger_stat); | 944 | kfree(cm->charger_stat); |
655 | err_no_charger_stat: | 945 | err_no_charger_stat: |
@@ -674,6 +964,9 @@ static int __devexit charger_manager_remove(struct platform_device *pdev) | |||
674 | if (desc->charger_regulators) | 964 | if (desc->charger_regulators) |
675 | regulator_bulk_free(desc->num_charger_regulators, | 965 | regulator_bulk_free(desc->num_charger_regulators, |
676 | desc->charger_regulators); | 966 | desc->charger_regulators); |
967 | |||
968 | power_supply_unregister(&cm->charger_psy); | ||
969 | kfree(cm->charger_psy.properties); | ||
677 | kfree(cm->charger_stat); | 970 | kfree(cm->charger_stat); |
678 | kfree(cm->desc); | 971 | kfree(cm->desc); |
679 | kfree(cm); | 972 | kfree(cm); |