in supply/bq2415x_charger.c [1523:1695]
static int bq2415x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
int num;
char *name = NULL;
struct bq2415x_device *bq;
struct device_node *np = client->dev.of_node;
struct bq2415x_platform_data *pdata = client->dev.platform_data;
const struct acpi_device_id *acpi_id = NULL;
struct power_supply *notify_psy = NULL;
union power_supply_propval prop;
if (!np && !pdata && !ACPI_HANDLE(&client->dev)) {
dev_err(&client->dev, "Neither devicetree, nor platform data, nor ACPI support\n");
return -ENODEV;
}
/* Get new ID for the new device */
mutex_lock(&bq2415x_id_mutex);
num = idr_alloc(&bq2415x_id, client, 0, 0, GFP_KERNEL);
mutex_unlock(&bq2415x_id_mutex);
if (num < 0)
return num;
if (id) {
name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num);
} else if (ACPI_HANDLE(&client->dev)) {
acpi_id =
acpi_match_device(client->dev.driver->acpi_match_table,
&client->dev);
if (!acpi_id) {
dev_err(&client->dev, "failed to match device name\n");
ret = -ENODEV;
goto error_1;
}
name = kasprintf(GFP_KERNEL, "%s-%d", acpi_id->id, num);
}
if (!name) {
dev_err(&client->dev, "failed to allocate device name\n");
ret = -ENOMEM;
goto error_1;
}
bq = devm_kzalloc(&client->dev, sizeof(*bq), GFP_KERNEL);
if (!bq) {
ret = -ENOMEM;
goto error_2;
}
i2c_set_clientdata(client, bq);
bq->id = num;
bq->dev = &client->dev;
if (id)
bq->chip = id->driver_data;
else if (ACPI_HANDLE(bq->dev))
bq->chip = acpi_id->driver_data;
bq->name = name;
bq->mode = BQ2415X_MODE_OFF;
bq->reported_mode = BQ2415X_MODE_OFF;
bq->autotimer = 0;
bq->automode = 0;
if (np || ACPI_HANDLE(bq->dev)) {
ret = device_property_read_u32(bq->dev,
"ti,current-limit",
&bq->init_data.current_limit);
if (ret)
goto error_2;
ret = device_property_read_u32(bq->dev,
"ti,weak-battery-voltage",
&bq->init_data.weak_battery_voltage);
if (ret)
goto error_2;
ret = device_property_read_u32(bq->dev,
"ti,battery-regulation-voltage",
&bq->init_data.battery_regulation_voltage);
if (ret)
goto error_2;
ret = device_property_read_u32(bq->dev,
"ti,charge-current",
&bq->init_data.charge_current);
if (ret)
goto error_2;
ret = device_property_read_u32(bq->dev,
"ti,termination-current",
&bq->init_data.termination_current);
if (ret)
goto error_2;
ret = device_property_read_u32(bq->dev,
"ti,resistor-sense",
&bq->init_data.resistor_sense);
if (ret)
goto error_2;
if (np)
bq->notify_node = of_parse_phandle(np,
"ti,usb-charger-detection", 0);
} else {
memcpy(&bq->init_data, pdata, sizeof(bq->init_data));
}
bq2415x_reset_chip(bq);
ret = bq2415x_power_supply_init(bq);
if (ret) {
dev_err(bq->dev, "failed to register power supply: %d\n", ret);
goto error_2;
}
ret = bq2415x_set_defaults(bq);
if (ret) {
dev_err(bq->dev, "failed to set default values: %d\n", ret);
goto error_3;
}
if (bq->notify_node || bq->init_data.notify_device) {
bq->nb.notifier_call = bq2415x_notifier_call;
ret = power_supply_reg_notifier(&bq->nb);
if (ret) {
dev_err(bq->dev, "failed to reg notifier: %d\n", ret);
goto error_3;
}
bq->automode = 1;
dev_info(bq->dev, "automode supported, waiting for events\n");
} else {
bq->automode = -1;
dev_info(bq->dev, "automode not supported\n");
}
/* Query for initial reported_mode and set it */
if (bq->nb.notifier_call) {
if (np) {
notify_psy = power_supply_get_by_phandle(np,
"ti,usb-charger-detection");
if (IS_ERR(notify_psy))
notify_psy = NULL;
} else if (bq->init_data.notify_device) {
notify_psy = power_supply_get_by_name(
bq->init_data.notify_device);
}
}
if (notify_psy) {
ret = power_supply_get_property(notify_psy,
POWER_SUPPLY_PROP_CURRENT_MAX, &prop);
power_supply_put(notify_psy);
if (ret == 0) {
bq2415x_update_reported_mode(bq, prop.intval);
bq2415x_set_mode(bq, bq->reported_mode);
}
}
INIT_DELAYED_WORK(&bq->work, bq2415x_timer_work);
bq2415x_set_autotimer(bq, 1);
dev_info(bq->dev, "driver registered\n");
return 0;
error_3:
bq2415x_power_supply_exit(bq);
error_2:
if (bq)
of_node_put(bq->notify_node);
kfree(name);
error_1:
mutex_lock(&bq2415x_id_mutex);
idr_remove(&bq2415x_id, num);
mutex_unlock(&bq2415x_id_mutex);
return ret;
}