/* About normal (not suspended) monitoring */ staticunsignedlong polling_jiffy = ULONG_MAX; /* ULONG_MAX: no polling */ staticunsignedlong next_polling; /* Next appointed polling time */ staticstruct workqueue_struct *cm_wq; /* init at driver add */ staticstruct delayed_work cm_monitor_work; /* init at driver add */
/** *is_batt_present-Seeifthebatterypresentsinplace. *@cm:theChargerManagerrepresentingthebattery.
*/ staticbool is_batt_present(struct charger_manager *cm)
{ union power_supply_propval val; struct power_supply *psy; bool present = false; int i, ret;
switch (cm->desc->battery_present) { case CM_BATTERY_PRESENT:
present = true; break; case CM_NO_BATTERY: break; case CM_FUEL_GAUGE:
psy = power_supply_get_by_name(cm->desc->psy_fuel_gauge); if (!psy) break;
ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT,
&val); if (ret == 0 && val.intval)
present = true;
power_supply_put(psy); break; case CM_CHARGER_STAT: for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
psy = power_supply_get_by_name(
cm->desc->psy_charger_stat[i]); if (!psy) {
dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
cm->desc->psy_charger_stat[i]); continue;
}
/** *is_ext_pwr_online-Seeifanexternalpowersourceisattachedtocharge *@cm:theChargerManagerrepresentingthebattery. * *Returnstrueifatleastoneofthechargersofthebatteryhasanexternal *powersourceattachedtochargethebatteryregardlessofwhetheritis *actuallychargingornot.
*/ staticbool is_ext_pwr_online(struct charger_manager *cm)
{ union power_supply_propval val; struct power_supply *psy; bool online = false; int i, ret;
/* If at least one of them has one, it's yes. */ for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]); if (!psy) {
dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
cm->desc->psy_charger_stat[i]); continue;
}
ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE,
&val);
power_supply_put(psy); if (ret == 0 && val.intval) {
online = true; break;
}
}
return online;
}
/** *get_batt_uV-Getthevoltagelevelofthebattery *@cm:theChargerManagerrepresentingthebattery. *@uV:thevoltagelevelreturned. * *Returns0ifthereisnoerror. *Returnsanegativevalueonerror.
*/ staticint get_batt_uV(struct charger_manager *cm, int *uV)
{ union power_supply_propval val; struct power_supply *fuel_gauge; int ret;
fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); if (!fuel_gauge) return -ENODEV;
ret = power_supply_get_property(fuel_gauge,
POWER_SUPPLY_PROP_VOLTAGE_NOW, &val);
power_supply_put(fuel_gauge); if (ret) return ret;
*uV = val.intval; return0;
}
/** *is_charging-Returnstrueifthebatteryisbeingcharged. *@cm:theChargerManagerrepresentingthebattery.
*/ staticbool is_charging(struct charger_manager *cm)
{ int i, ret; bool charging = false; struct power_supply *psy; union power_supply_propval val;
/* If there is no battery, it cannot be charged */ if (!is_batt_present(cm)) returnfalse;
/* If at least one of the charger is charging, return yes */ for (i = 0; cm->desc->psy_charger_stat[i]; i++) { /* 1. The charger should not be DISABLED */ if (cm->emergency_stop) continue; if (!cm->charger_enabled) continue;
psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]); if (!psy) {
dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
cm->desc->psy_charger_stat[i]); continue;
}
/* 2. The charger should be online (ext-power) */
ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE,
&val); if (ret) {
dev_warn(cm->dev, "Cannot read ONLINE value from %s\n",
cm->desc->psy_charger_stat[i]);
power_supply_put(psy); continue;
} if (val.intval == 0) {
power_supply_put(psy); continue;
}
/* *3.ThechargershouldnotbeFULL,DISCHARGING, *orNOT_CHARGING.
*/
ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS,
&val);
power_supply_put(psy); if (ret) {
dev_warn(cm->dev, "Cannot read STATUS value from %s\n",
cm->desc->psy_charger_stat[i]); continue;
} if (val.intval == POWER_SUPPLY_STATUS_FULL ||
val.intval == POWER_SUPPLY_STATUS_DISCHARGING ||
val.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) continue;
/* Then, this is charging. */
charging = true; break;
}
return charging;
}
/** *is_full_charged-Returnstrueifthebatteryisfullycharged. *@cm:theChargerManagerrepresentingthebattery.
*/ staticbool is_full_charged(struct charger_manager *cm)
{ struct charger_desc *desc = cm->desc; union power_supply_propval val; struct power_supply *fuel_gauge; bool is_full = false; int ret = 0; int uV;
/* If there is no battery, it cannot be charged */ if (!is_batt_present(cm)) returnfalse;
fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); if (!fuel_gauge) returnfalse;
/* Full, if it's over the fullbatt voltage */ if (desc->fullbatt_uV > 0) {
ret = get_batt_uV(cm, &uV); if (!ret) { /* Battery is already full, checks voltage drop. */ if (cm->battery_status == POWER_SUPPLY_STATUS_FULL
&& desc->fullbatt_vchkdrop_uV)
uV += desc->fullbatt_vchkdrop_uV; if (uV >= desc->fullbatt_uV) returntrue;
}
}
if (desc->fullbatt_full_capacity > 0) {
val.intval = 0;
/* Not full if capacity of fuel gauge isn't full */
ret = power_supply_get_property(fuel_gauge,
POWER_SUPPLY_PROP_CHARGE_FULL, &val); if (!ret && val.intval > desc->fullbatt_full_capacity) {
is_full = true; goto out;
}
}
/* Full, if the capacity is more than fullbatt_soc */ if (desc->fullbatt_soc > 0) {
val.intval = 0;
ret = power_supply_get_property(fuel_gauge,
POWER_SUPPLY_PROP_CAPACITY, &val); if (!ret && val.intval >= desc->fullbatt_soc) {
is_full = true; goto out;
}
}
if (duration > desc->discharging_max_duration_ms) {
dev_info(cm->dev, "Discharging duration exceed %ums\n",
desc->discharging_max_duration_ms);
ret = true;
}
}
return ret;
}
staticint cm_get_battery_temperature_by_psy(struct charger_manager *cm, int *temp)
{ struct power_supply *fuel_gauge; int ret;
fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); if (!fuel_gauge) return -ENODEV;
ret = power_supply_get_property(fuel_gauge,
POWER_SUPPLY_PROP_TEMP,
(union power_supply_propval *)temp);
power_supply_put(fuel_gauge);
return ret;
}
staticint cm_get_battery_temperature(struct charger_manager *cm, int *temp)
{ int ret;
if (!cm->desc->measure_battery_temp) return -ENODEV;
#ifdef CONFIG_THERMAL if (cm->tzd_batt) {
ret = thermal_zone_get_temp(cm->tzd_batt, temp); if (!ret) /* Calibrate temperature unit */
*temp /= 100;
} else #endif
{ /* if-else continued from CONFIG_THERMAL */
ret = cm_get_battery_temperature_by_psy(cm, temp);
}
return ret;
}
staticint cm_check_thermal_status(struct charger_manager *cm)
{ struct charger_desc *desc = cm->desc; int temp, upper_limit, lower_limit; int ret = 0;
ret = cm_get_battery_temperature(cm, &temp); if (ret) { /* FIXME: *Noinformationofbatterytemperaturemight *occurhazardousresult.Wehavetohandleit *dependingonbatterytype.
*/
dev_err(cm->dev, "Failed to get battery temperature\n"); return0;
}
staticint charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val)
{ struct charger_manager *cm = power_supply_get_drvdata(psy); struct charger_desc *desc = cm->desc; struct power_supply *fuel_gauge = NULL; int ret = 0; int uV;
switch (psp) { case POWER_SUPPLY_PROP_STATUS:
val->intval = cm->battery_status; break; case POWER_SUPPLY_PROP_HEALTH: if (cm->emergency_stop == CM_BATT_OVERHEAT)
val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; elseif (cm->emergency_stop == CM_BATT_COLD)
val->intval = POWER_SUPPLY_HEALTH_COLD; else
val->intval = POWER_SUPPLY_HEALTH_GOOD; break; case POWER_SUPPLY_PROP_PRESENT: if (is_batt_present(cm))
val->intval = 1; else
val->intval = 0; break; case POWER_SUPPLY_PROP_VOLTAGE_NOW:
ret = get_batt_uV(cm, &val->intval); break; case POWER_SUPPLY_PROP_CURRENT_NOW:
fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); if (!fuel_gauge) {
ret = -ENODEV; break;
}
ret = power_supply_get_property(fuel_gauge,
POWER_SUPPLY_PROP_CURRENT_NOW, val); break; case POWER_SUPPLY_PROP_TEMP: return cm_get_battery_temperature(cm, &val->intval); case POWER_SUPPLY_PROP_CAPACITY: if (!is_batt_present(cm)) { /* There is no battery. Assume 100% */
val->intval = 100; break;
}
fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); if (!fuel_gauge) {
ret = -ENODEV; break;
}
ret = power_supply_get_property(fuel_gauge,
POWER_SUPPLY_PROP_CAPACITY, val); if (ret) break;
if (val->intval > 100) {
val->intval = 100; break;
} if (val->intval < 0)
val->intval = 0;
/* Do not adjust SOC when charging: voltage is overrated */ if (is_charging(cm)) break;
/* *Ifthecapacityvalueisinconsistent,calibrateitbaseon *thebatteryvoltagevaluesandthethresholdsgivenasdesc
*/
ret = get_batt_uV(cm, &uV); if (ret) { /* Voltage information not available. No calibration */
ret = 0; break;
}
if (time_after(next_polling, jiffies))
CM_MIN_VALID(wakeup_ms,
jiffies_to_msecs(next_polling - jiffies));
mutex_lock(&cm_list_mtx);
list_for_each_entry(cm, &cm_list, entry) { /* Skip if polling is not required for this CM */ if (!is_polling_required(cm) && !cm->emergency_stop) continue;
timer_req++; if (cm->desc->polling_interval_ms == 0) continue;
CM_MIN_VALID(wakeup_ms, cm->desc->polling_interval_ms);
}
mutex_unlock(&cm_list_mtx);
cable->extcon_dev = extcon_get_extcon_dev(cable->extcon_name); if (IS_ERR(cable->extcon_dev)) {
pr_err("Cannot find extcon_dev for %s (cable: %s)\n",
cable->extcon_name, cable->name); return PTR_ERR(cable->extcon_dev);
}
for (i = 0; i < ARRAY_SIZE(extcon_mapping); i++) { if (!strcmp(cable->name, extcon_mapping[i].name)) {
extcon_type = extcon_mapping[i].extcon_type; break;
}
} if (extcon_type == EXTCON_NONE) {
pr_err("Cannot find cable for type %s", cable->name); return -EINVAL;
}
cable->extcon_type = extcon_type;
ret = devm_extcon_register_notifier(cm->dev, cable->extcon_dev,
cable->extcon_type, &cable->nb); if (ret < 0) {
pr_err("Cannot register extcon_dev for %s (cable: %s)\n",
cable->extcon_name, cable->name); return ret;
}
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) { /* *Atleast,onechargeriscontrolledby *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);
}
if (!desc->charger_regulators[i].externally_control ||
!chargers_externally_control)
chargers_externally_control = 0;
dev_info(cm->dev, "'%s' regulator's externally_control is %d\n",
charger->regulator_name, charger->externally_control);
}
if (chargers_externally_control) {
dev_err(cm->dev, "Cannot register regulator because charger-manager must need at least one charger for charging battery\n"); return -EINVAL;
}
/* chargers */
num_chgs = of_property_count_strings(np, "cm-chargers"); if (num_chgs > 0) { int i;
/* Allocate empty bin at the tail of array */
desc->psy_charger_stat = devm_kcalloc(dev,
num_chgs + 1, sizeof(char *),
GFP_KERNEL); if (!desc->psy_charger_stat) return ERR_PTR(-ENOMEM);
for (i = 0; i < num_chgs; i++)
of_property_read_string_index(np, "cm-chargers",
i, &desc->psy_charger_stat[i]);
}
/* *Someofthefollowingdonotneedtobeerrors. *Usersmayintentionallyignorethosefeatures.
*/ if (desc->fullbatt_uV == 0) {
dev_info(&pdev->dev, "Ignoring full-battery voltage threshold as it is not supplied\n");
} if (!desc->fullbatt_vchkdrop_uV) {
dev_info(&pdev->dev, "Disabling full-battery voltage drop checking mechanism as it is not supplied\n");
desc->fullbatt_vchkdrop_uV = 0;
} if (desc->fullbatt_soc == 0) {
dev_info(&pdev->dev, "Ignoring full-battery soc(state of charge) threshold as it is not supplied\n");
} if (desc->fullbatt_full_capacity == 0) {
dev_info(&pdev->dev, "Ignoring full-battery full capacity threshold as it is not supplied\n");
}
/* Allocate for psy properties because they may vary */
properties = devm_kcalloc(&pdev->dev,
ARRAY_SIZE(default_charger_props) +
NUM_CHARGER_PSY_OPTIONAL, sizeof(*properties), GFP_KERNEL); if (!properties) return -ENOMEM;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.