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