in supply/ab8500_charger.c [3422:3718]
static int ab8500_charger_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct component_match *match = NULL;
struct power_supply_config ac_psy_cfg = {}, usb_psy_cfg = {};
struct ab8500_charger *di;
int charger_status;
int i, irq;
int ret;
di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL);
if (!di)
return -ENOMEM;
di->bm = &ab8500_bm_data;
di->autopower_cfg = of_property_read_bool(np, "autopower_cfg");
/* get parent data */
di->dev = dev;
di->parent = dev_get_drvdata(pdev->dev.parent);
/* Get ADC channels */
di->adc_main_charger_v = devm_iio_channel_get(dev, "main_charger_v");
if (IS_ERR(di->adc_main_charger_v)) {
ret = dev_err_probe(dev, PTR_ERR(di->adc_main_charger_v),
"failed to get ADC main charger voltage\n");
return ret;
}
di->adc_main_charger_c = devm_iio_channel_get(dev, "main_charger_c");
if (IS_ERR(di->adc_main_charger_c)) {
ret = dev_err_probe(dev, PTR_ERR(di->adc_main_charger_c),
"failed to get ADC main charger current\n");
return ret;
}
di->adc_vbus_v = devm_iio_channel_get(dev, "vbus_v");
if (IS_ERR(di->adc_vbus_v)) {
ret = dev_err_probe(dev, PTR_ERR(di->adc_vbus_v),
"failed to get ADC USB charger voltage\n");
return ret;
}
di->adc_usb_charger_c = devm_iio_channel_get(dev, "usb_charger_c");
if (IS_ERR(di->adc_usb_charger_c)) {
ret = dev_err_probe(dev, PTR_ERR(di->adc_usb_charger_c),
"failed to get ADC USB charger current\n");
return ret;
}
/*
* VDD ADC supply needs to be enabled from this driver when there
* is a charger connected to avoid erroneous BTEMP_HIGH/LOW
* interrupts during charging
*/
di->regu = devm_regulator_get(dev, "vddadc");
if (IS_ERR(di->regu)) {
ret = PTR_ERR(di->regu);
dev_err(dev, "failed to get vddadc regulator\n");
return ret;
}
/* Request interrupts */
for (i = 0; i < ARRAY_SIZE(ab8500_charger_irq); i++) {
irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
if (irq < 0)
return irq;
ret = devm_request_threaded_irq(dev,
irq, NULL, ab8500_charger_irq[i].isr,
IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT,
ab8500_charger_irq[i].name, di);
if (ret != 0) {
dev_err(dev, "failed to request %s IRQ %d: %d\n"
, ab8500_charger_irq[i].name, irq, ret);
return ret;
}
dev_dbg(dev, "Requested %s IRQ %d: %d\n",
ab8500_charger_irq[i].name, irq, ret);
}
/* initialize lock */
spin_lock_init(&di->usb_state.usb_lock);
mutex_init(&di->usb_ipt_crnt_lock);
di->autopower = false;
di->invalid_charger_detect_state = 0;
/* AC and USB supply config */
ac_psy_cfg.of_node = np;
ac_psy_cfg.supplied_to = supply_interface;
ac_psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface);
ac_psy_cfg.drv_data = &di->ac_chg;
usb_psy_cfg.of_node = np;
usb_psy_cfg.supplied_to = supply_interface;
usb_psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface);
usb_psy_cfg.drv_data = &di->usb_chg;
/* AC supply */
/* ux500_charger sub-class */
di->ac_chg.ops.enable = &ab8500_charger_ac_en;
di->ac_chg.ops.check_enable = &ab8500_charger_ac_check_enable;
di->ac_chg.ops.kick_wd = &ab8500_charger_watchdog_kick;
di->ac_chg.ops.update_curr = &ab8500_charger_update_charger_current;
di->ac_chg.max_out_volt_uv = ab8500_charger_voltage_map[
ARRAY_SIZE(ab8500_charger_voltage_map) - 1];
di->ac_chg.max_out_curr_ua =
ab8500_charge_output_curr_map[ARRAY_SIZE(ab8500_charge_output_curr_map) - 1];
di->ac_chg.wdt_refresh = CHG_WD_INTERVAL;
/*
* The AB8505 only supports USB charging. If we are not the
* AB8505, register an AC charger.
*
* TODO: if this should be opt-in, add DT properties for this.
*/
if (!is_ab8505(di->parent))
di->ac_chg.enabled = true;
di->ac_chg.external = false;
/* USB supply */
/* ux500_charger sub-class */
di->usb_chg.ops.enable = &ab8500_charger_usb_en;
di->usb_chg.ops.check_enable = &ab8500_charger_usb_check_enable;
di->usb_chg.ops.kick_wd = &ab8500_charger_watchdog_kick;
di->usb_chg.ops.update_curr = &ab8500_charger_update_charger_current;
di->usb_chg.max_out_volt_uv = ab8500_charger_voltage_map[
ARRAY_SIZE(ab8500_charger_voltage_map) - 1];
di->usb_chg.max_out_curr_ua =
ab8500_charge_output_curr_map[ARRAY_SIZE(ab8500_charge_output_curr_map) - 1];
di->usb_chg.wdt_refresh = CHG_WD_INTERVAL;
di->usb_chg.external = false;
di->usb_state.usb_current_ua = -1;
mutex_init(&di->charger_attached_mutex);
/* Init work for HW failure check */
INIT_DEFERRABLE_WORK(&di->check_hw_failure_work,
ab8500_charger_check_hw_failure_work);
INIT_DEFERRABLE_WORK(&di->check_usbchgnotok_work,
ab8500_charger_check_usbchargernotok_work);
INIT_DELAYED_WORK(&di->ac_charger_attached_work,
ab8500_charger_ac_attached_work);
INIT_DELAYED_WORK(&di->usb_charger_attached_work,
ab8500_charger_usb_attached_work);
/*
* For ABB revision 1.0 and 1.1 there is a bug in the watchdog
* logic. That means we have to continuously kick the charger
* watchdog even when no charger is connected. This is only
* valid once the AC charger has been enabled. This is
* a bug that is not handled by the algorithm and the
* watchdog have to be kicked by the charger driver
* when the AC charger is disabled
*/
INIT_DEFERRABLE_WORK(&di->kick_wd_work,
ab8500_charger_kick_watchdog_work);
INIT_DEFERRABLE_WORK(&di->check_vbat_work,
ab8500_charger_check_vbat_work);
INIT_DELAYED_WORK(&di->attach_work,
ab8500_charger_usb_link_attach_work);
INIT_DELAYED_WORK(&di->usb_state_changed_work,
ab8500_charger_usb_state_changed_work);
INIT_DELAYED_WORK(&di->vbus_drop_end_work,
ab8500_charger_vbus_drop_end_work);
/* Init work for charger detection */
INIT_WORK(&di->usb_link_status_work,
ab8500_charger_usb_link_status_work);
INIT_WORK(&di->ac_work, ab8500_charger_ac_work);
INIT_WORK(&di->detect_usb_type_work,
ab8500_charger_detect_usb_type_work);
/* Init work for checking HW status */
INIT_WORK(&di->check_main_thermal_prot_work,
ab8500_charger_check_main_thermal_prot_work);
INIT_WORK(&di->check_usb_thermal_prot_work,
ab8500_charger_check_usb_thermal_prot_work);
/* Initialize OVV, and other registers */
ret = ab8500_charger_init_hw_registers(di);
if (ret) {
dev_err(dev, "failed to initialize ABB registers\n");
return ret;
}
/* Register AC charger class */
if (di->ac_chg.enabled) {
di->ac_chg.psy = devm_power_supply_register(dev,
&ab8500_ac_chg_desc,
&ac_psy_cfg);
if (IS_ERR(di->ac_chg.psy)) {
dev_err(dev, "failed to register AC charger\n");
return PTR_ERR(di->ac_chg.psy);
}
}
/* Register USB charger class */
di->usb_chg.psy = devm_power_supply_register(dev,
&ab8500_usb_chg_desc,
&usb_psy_cfg);
if (IS_ERR(di->usb_chg.psy)) {
dev_err(dev, "failed to register USB charger\n");
return PTR_ERR(di->usb_chg.psy);
}
/*
* Check what battery we have, since we always have the USB
* psy, use that as a handle.
*/
ret = ab8500_bm_of_probe(di->usb_chg.psy, di->bm);
if (ret)
return dev_err_probe(dev, ret,
"failed to get battery information\n");
/* Identify the connected charger types during startup */
charger_status = ab8500_charger_detect_chargers(di, true);
if (charger_status & AC_PW_CONN) {
di->ac.charger_connected = 1;
di->ac_conn = true;
ab8500_power_supply_changed(di, di->ac_chg.psy);
sysfs_notify(&di->ac_chg.psy->dev.kobj, NULL, "present");
}
platform_set_drvdata(pdev, di);
/* Create something that will match the subdrivers when we bind */
for (i = 0; i < ARRAY_SIZE(ab8500_charger_component_drivers); i++) {
struct device_driver *drv = &ab8500_charger_component_drivers[i]->driver;
struct device *p = NULL, *d;
while ((d = platform_find_device_by_driver(p, drv))) {
put_device(p);
component_match_add(dev, &match,
ab8500_charger_compare_dev, d);
p = d;
}
put_device(p);
}
if (!match) {
dev_err(dev, "no matching components\n");
ret = -ENODEV;
goto remove_ab8500_bm;
}
if (IS_ERR(match)) {
dev_err(dev, "could not create component match\n");
ret = PTR_ERR(match);
goto remove_ab8500_bm;
}
/* Notifier for external charger enabling */
if (!di->ac_chg.enabled)
blocking_notifier_chain_register(
&charger_notifier_list, &charger_nb);
di->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2);
if (IS_ERR_OR_NULL(di->usb_phy)) {
dev_err(dev, "failed to get usb transceiver\n");
ret = -EINVAL;
goto out_charger_notifier;
}
di->nb.notifier_call = ab8500_charger_usb_notifier_call;
ret = usb_register_notifier(di->usb_phy, &di->nb);
if (ret) {
dev_err(dev, "failed to register usb notifier\n");
goto put_usb_phy;
}
ret = component_master_add_with_match(&pdev->dev,
&ab8500_charger_comp_ops,
match);
if (ret) {
dev_err(dev, "failed to add component master\n");
goto free_notifier;
}
return 0;
free_notifier:
usb_unregister_notifier(di->usb_phy, &di->nb);
put_usb_phy:
usb_put_phy(di->usb_phy);
out_charger_notifier:
if (!di->ac_chg.enabled)
blocking_notifier_chain_unregister(
&charger_notifier_list, &charger_nb);
remove_ab8500_bm:
ab8500_bm_of_remove(di->usb_chg.psy, di->bm);
return ret;
}