static int ab8500_charger_probe()

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;
}