in supply/charger-manager.c [1424:1632]
static int charger_manager_probe(struct platform_device *pdev)
{
struct charger_desc *desc = cm_get_drv_data(pdev);
struct charger_manager *cm;
int ret, i = 0;
union power_supply_propval val;
struct power_supply *fuel_gauge;
enum power_supply_property *properties;
size_t num_properties;
struct power_supply_config psy_cfg = {};
if (IS_ERR(desc)) {
dev_err(&pdev->dev, "No platform data (desc) found\n");
return PTR_ERR(desc);
}
cm = devm_kzalloc(&pdev->dev, sizeof(*cm), GFP_KERNEL);
if (!cm)
return -ENOMEM;
/* Basic Values. Unspecified are Null or 0 */
cm->dev = &pdev->dev;
cm->desc = desc;
psy_cfg.drv_data = cm;
/* Initialize alarm timer */
if (alarmtimer_get_rtcdev()) {
cm_timer = devm_kzalloc(cm->dev, sizeof(*cm_timer), GFP_KERNEL);
if (!cm_timer)
return -ENOMEM;
alarm_init(cm_timer, ALARM_BOOTTIME, cm_timer_func);
}
/*
* Some of the following do not need to be errors.
* Users may intentionally ignore those features.
*/
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");
}
if (!desc->charger_regulators || desc->num_charger_regulators < 1) {
dev_err(&pdev->dev, "charger_regulators undefined\n");
return -EINVAL;
}
if (!desc->psy_charger_stat || !desc->psy_charger_stat[0]) {
dev_err(&pdev->dev, "No power supply defined\n");
return -EINVAL;
}
if (!desc->psy_fuel_gauge) {
dev_err(&pdev->dev, "No fuel gauge power supply defined\n");
return -EINVAL;
}
/* Check if charger's supplies are present at probe */
for (i = 0; desc->psy_charger_stat[i]; i++) {
struct power_supply *psy;
psy = power_supply_get_by_name(desc->psy_charger_stat[i]);
if (!psy) {
dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
desc->psy_charger_stat[i]);
return -ENODEV;
}
power_supply_put(psy);
}
if (cm->desc->polling_mode != CM_POLL_DISABLE &&
(desc->polling_interval_ms == 0 ||
msecs_to_jiffies(desc->polling_interval_ms) <= CM_JIFFIES_SMALL)) {
dev_err(&pdev->dev, "polling_interval_ms is too small\n");
return -EINVAL;
}
if (!desc->charging_max_duration_ms ||
!desc->discharging_max_duration_ms) {
dev_info(&pdev->dev, "Cannot limit charging duration checking mechanism to prevent overcharge/overheat and control discharging duration\n");
desc->charging_max_duration_ms = 0;
desc->discharging_max_duration_ms = 0;
}
platform_set_drvdata(pdev, cm);
memcpy(&cm->charger_psy_desc, &psy_default, sizeof(psy_default));
if (!desc->psy_name)
strncpy(cm->psy_name_buf, psy_default.name, PSY_NAME_MAX);
else
strncpy(cm->psy_name_buf, desc->psy_name, PSY_NAME_MAX);
cm->charger_psy_desc.name = cm->psy_name_buf;
/* 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;
memcpy(properties, default_charger_props,
sizeof(enum power_supply_property) *
ARRAY_SIZE(default_charger_props));
num_properties = ARRAY_SIZE(default_charger_props);
/* Find which optional psy-properties are available */
fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge);
if (!fuel_gauge) {
dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
desc->psy_fuel_gauge);
return -ENODEV;
}
if (!power_supply_get_property(fuel_gauge,
POWER_SUPPLY_PROP_CHARGE_FULL, &val)) {
properties[num_properties] =
POWER_SUPPLY_PROP_CHARGE_FULL;
num_properties++;
}
if (!power_supply_get_property(fuel_gauge,
POWER_SUPPLY_PROP_CHARGE_NOW, &val)) {
properties[num_properties] =
POWER_SUPPLY_PROP_CHARGE_NOW;
num_properties++;
}
if (!power_supply_get_property(fuel_gauge,
POWER_SUPPLY_PROP_CURRENT_NOW,
&val)) {
properties[num_properties] =
POWER_SUPPLY_PROP_CURRENT_NOW;
num_properties++;
}
ret = cm_init_thermal_data(cm, fuel_gauge, properties, &num_properties);
if (ret) {
dev_err(&pdev->dev, "Failed to initialize thermal data\n");
cm->desc->measure_battery_temp = false;
}
power_supply_put(fuel_gauge);
cm->charger_psy_desc.properties = properties;
cm->charger_psy_desc.num_properties = num_properties;
/* Register sysfs entry for charger(regulator) */
ret = charger_manager_prepare_sysfs(cm);
if (ret < 0) {
dev_err(&pdev->dev,
"Cannot prepare sysfs entry of regulators\n");
return ret;
}
psy_cfg.attr_grp = desc->sysfs_groups;
cm->charger_psy = power_supply_register(&pdev->dev,
&cm->charger_psy_desc,
&psy_cfg);
if (IS_ERR(cm->charger_psy)) {
dev_err(&pdev->dev, "Cannot register charger-manager with name \"%s\"\n",
cm->charger_psy_desc.name);
return PTR_ERR(cm->charger_psy);
}
/* Register extcon device for charger cable */
ret = charger_manager_register_extcon(cm);
if (ret < 0) {
dev_err(&pdev->dev, "Cannot initialize extcon device\n");
goto err_reg_extcon;
}
/* Add to the list */
mutex_lock(&cm_list_mtx);
list_add(&cm->entry, &cm_list);
mutex_unlock(&cm_list_mtx);
/*
* Charger-manager is capable of waking up the system from sleep
* when event is happened through cm_notify_event()
*/
device_init_wakeup(&pdev->dev, true);
device_set_wakeup_capable(&pdev->dev, false);
/*
* Charger-manager have to check the charging state right after
* initialization of charger-manager and then update current charging
* state.
*/
cm_monitor();
schedule_work(&setup_polling);
return 0;
err_reg_extcon:
for (i = 0; i < desc->num_charger_regulators; i++)
regulator_put(desc->charger_regulators[i].consumer);
power_supply_unregister(cm->charger_psy);
return ret;
}