static int twl4030_bci_probe()

in supply/twl4030_charger.c [976:1110]


static int twl4030_bci_probe(struct platform_device *pdev)
{
	struct twl4030_bci *bci;
	const struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data;
	int ret;
	u32 reg;

	bci = devm_kzalloc(&pdev->dev, sizeof(*bci), GFP_KERNEL);
	if (bci == NULL)
		return -ENOMEM;

	if (!pdata)
		pdata = twl4030_bci_parse_dt(&pdev->dev);

	bci->ichg_eoc = 80100; /* Stop charging when current drops to here */
	bci->ichg_lo = 241000; /* Low threshold */
	bci->ichg_hi = 500000; /* High threshold */
	bci->ac_cur = 500000; /* 500mA */
	if (allow_usb)
		bci->usb_cur_target = 500000;  /* 500mA */
	else
		bci->usb_cur_target = 100000;  /* 100mA */
	bci->usb_mode = CHARGE_AUTO;
	bci->ac_mode = CHARGE_AUTO;

	bci->dev = &pdev->dev;
	bci->irq_chg = platform_get_irq(pdev, 0);
	bci->irq_bci = platform_get_irq(pdev, 1);

	platform_set_drvdata(pdev, bci);

	INIT_WORK(&bci->work, twl4030_bci_usb_work);
	INIT_DELAYED_WORK(&bci->current_worker, twl4030_current_worker);

	bci->channel_vac = devm_iio_channel_get(&pdev->dev, "vac");
	if (IS_ERR(bci->channel_vac)) {
		ret = PTR_ERR(bci->channel_vac);
		if (ret == -EPROBE_DEFER)
			return ret;	/* iio not ready */
		dev_warn(&pdev->dev, "could not request vac iio channel (%d)",
			ret);
		bci->channel_vac = NULL;
	}

	if (bci->dev->of_node) {
		struct device_node *phynode;

		phynode = of_get_compatible_child(bci->dev->of_node->parent,
						  "ti,twl4030-usb");
		if (phynode) {
			bci->usb_nb.notifier_call = twl4030_bci_usb_ncb;
			bci->transceiver = devm_usb_get_phy_by_node(
				bci->dev, phynode, &bci->usb_nb);
			of_node_put(phynode);
			if (IS_ERR(bci->transceiver)) {
				ret = PTR_ERR(bci->transceiver);
				if (ret == -EPROBE_DEFER)
					return ret;	/* phy not ready */
				dev_warn(&pdev->dev, "could not request transceiver (%d)",
					ret);
				bci->transceiver = NULL;
			}
		}
	}

	bci->ac = devm_power_supply_register(&pdev->dev, &twl4030_bci_ac_desc,
					     NULL);
	if (IS_ERR(bci->ac)) {
		ret = PTR_ERR(bci->ac);
		dev_err(&pdev->dev, "failed to register ac: %d\n", ret);
		return ret;
	}

	bci->usb = devm_power_supply_register(&pdev->dev, &twl4030_bci_usb_desc,
					      NULL);
	if (IS_ERR(bci->usb)) {
		ret = PTR_ERR(bci->usb);
		dev_err(&pdev->dev, "failed to register usb: %d\n", ret);
		return ret;
	}

	ret = devm_request_threaded_irq(&pdev->dev, bci->irq_chg, NULL,
			twl4030_charger_interrupt, IRQF_ONESHOT, pdev->name,
			bci);
	if (ret < 0) {
		dev_err(&pdev->dev, "could not request irq %d, status %d\n",
			bci->irq_chg, ret);
		return ret;
	}

	ret = devm_request_threaded_irq(&pdev->dev, bci->irq_bci, NULL,
			twl4030_bci_interrupt, IRQF_ONESHOT, pdev->name, bci);
	if (ret < 0) {
		dev_err(&pdev->dev, "could not request irq %d, status %d\n",
			bci->irq_bci, ret);
		return ret;
	}

	/* Enable interrupts now. */
	reg = ~(u32)(TWL4030_ICHGLOW | TWL4030_ICHGEOC | TWL4030_TBATOR2 |
		TWL4030_TBATOR1 | TWL4030_BATSTS);
	ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg,
			       TWL4030_INTERRUPTS_BCIIMR1A);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
		return ret;
	}

	reg = ~(u32)(TWL4030_VBATOV | TWL4030_VBUSOV | TWL4030_ACCHGOV);
	ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg,
			       TWL4030_INTERRUPTS_BCIIMR2A);
	if (ret < 0)
		dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n", ret);

	twl4030_charger_update_current(bci);
	if (device_create_file(&bci->usb->dev, &dev_attr_mode))
		dev_warn(&pdev->dev, "could not create sysfs file\n");
	if (device_create_file(&bci->ac->dev, &dev_attr_mode))
		dev_warn(&pdev->dev, "could not create sysfs file\n");

	twl4030_charger_enable_ac(bci, true);
	if (!IS_ERR_OR_NULL(bci->transceiver))
		twl4030_bci_usb_ncb(&bci->usb_nb,
				    bci->transceiver->last_event,
				    NULL);
	else
		twl4030_charger_enable_usb(bci, false);
	if (pdata)
		twl4030_charger_enable_backup(pdata->bb_uvolt,
					      pdata->bb_uamp);
	else
		twl4030_charger_enable_backup(0, 0);

	return 0;
}