in supply/rt9455_charger.c [1584:1699]
static int rt9455_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = client->adapter;
struct device *dev = &client->dev;
struct rt9455_info *info;
struct power_supply_config rt9455_charger_config = {};
/*
* Mandatory device-specific data values. Also, VOREG and boost output
* voltage are mandatory values, but they are stored in rt9455_info
* structure.
*/
u32 ichrg, ieoc_percentage;
/* Optional device-specific data values. */
u32 mivr = -1, iaicr = -1;
int i, ret;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(dev, "No support for SMBUS_BYTE_DATA\n");
return -ENODEV;
}
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->client = client;
i2c_set_clientdata(client, info);
info->regmap = devm_regmap_init_i2c(client,
&rt9455_regmap_config);
if (IS_ERR(info->regmap)) {
dev_err(dev, "Failed to initialize register map\n");
return -EINVAL;
}
for (i = 0; i < F_MAX_FIELDS; i++) {
info->regmap_fields[i] =
devm_regmap_field_alloc(dev, info->regmap,
rt9455_reg_fields[i]);
if (IS_ERR(info->regmap_fields[i])) {
dev_err(dev,
"Failed to allocate regmap field = %d\n", i);
return PTR_ERR(info->regmap_fields[i]);
}
}
ret = rt9455_discover_charger(info, &ichrg, &ieoc_percentage,
&mivr, &iaicr);
if (ret) {
dev_err(dev, "Failed to discover charger\n");
return ret;
}
#if IS_ENABLED(CONFIG_USB_PHY)
info->usb_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
if (IS_ERR(info->usb_phy)) {
dev_err(dev, "Failed to get USB transceiver\n");
} else {
info->nb.notifier_call = rt9455_usb_event;
ret = usb_register_notifier(info->usb_phy, &info->nb);
if (ret) {
dev_err(dev, "Failed to register USB notifier\n");
/*
* If usb_register_notifier() fails, set notifier_call
* to NULL, to avoid calling usb_unregister_notifier().
*/
info->nb.notifier_call = NULL;
}
}
#endif
INIT_DEFERRABLE_WORK(&info->pwr_rdy_work, rt9455_pwr_rdy_work_callback);
INIT_DEFERRABLE_WORK(&info->max_charging_time_work,
rt9455_max_charging_time_work_callback);
INIT_DEFERRABLE_WORK(&info->batt_presence_work,
rt9455_batt_presence_work_callback);
rt9455_charger_config.of_node = dev->of_node;
rt9455_charger_config.drv_data = info;
rt9455_charger_config.supplied_to = rt9455_charger_supplied_to;
rt9455_charger_config.num_supplicants =
ARRAY_SIZE(rt9455_charger_supplied_to);
ret = devm_request_threaded_irq(dev, client->irq, NULL,
rt9455_irq_handler_thread,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
RT9455_DRIVER_NAME, info);
if (ret) {
dev_err(dev, "Failed to register IRQ handler\n");
goto put_usb_notifier;
}
ret = rt9455_hw_init(info, ichrg, ieoc_percentage, mivr, iaicr);
if (ret) {
dev_err(dev, "Failed to set charger to its default values\n");
goto put_usb_notifier;
}
info->charger = devm_power_supply_register(dev, &rt9455_charger_desc,
&rt9455_charger_config);
if (IS_ERR(info->charger)) {
dev_err(dev, "Failed to register charger\n");
ret = PTR_ERR(info->charger);
goto put_usb_notifier;
}
return 0;
put_usb_notifier:
#if IS_ENABLED(CONFIG_USB_PHY)
if (info->nb.notifier_call) {
usb_unregister_notifier(info->usb_phy, &info->nb);
info->nb.notifier_call = NULL;
}
#endif
return ret;
}