in chipidea/core.c [1000:1228]
static int ci_hdrc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct ci_hdrc *ci;
struct resource *res;
void __iomem *base;
int ret;
enum usb_dr_mode dr_mode;
if (!dev_get_platdata(dev)) {
dev_err(dev, "platform data missing\n");
return -ENODEV;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
ci = devm_kzalloc(dev, sizeof(*ci), GFP_KERNEL);
if (!ci)
return -ENOMEM;
spin_lock_init(&ci->lock);
ci->dev = dev;
ci->platdata = dev_get_platdata(dev);
ci->imx28_write_fix = !!(ci->platdata->flags &
CI_HDRC_IMX28_WRITE_FIX);
ci->supports_runtime_pm = !!(ci->platdata->flags &
CI_HDRC_SUPPORTS_RUNTIME_PM);
platform_set_drvdata(pdev, ci);
ret = hw_device_init(ci, base);
if (ret < 0) {
dev_err(dev, "can't initialize hardware\n");
return -ENODEV;
}
ret = ci_ulpi_init(ci);
if (ret)
return ret;
if (ci->platdata->phy) {
ci->phy = ci->platdata->phy;
} else if (ci->platdata->usb_phy) {
ci->usb_phy = ci->platdata->usb_phy;
} else {
/* Look for a generic PHY first */
ci->phy = devm_phy_get(dev->parent, "usb-phy");
if (PTR_ERR(ci->phy) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
goto ulpi_exit;
} else if (IS_ERR(ci->phy)) {
ci->phy = NULL;
}
/* Look for a legacy USB PHY from device-tree next */
if (!ci->phy) {
ci->usb_phy = devm_usb_get_phy_by_phandle(dev->parent,
"phys", 0);
if (PTR_ERR(ci->usb_phy) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
goto ulpi_exit;
} else if (IS_ERR(ci->usb_phy)) {
ci->usb_phy = NULL;
}
}
/* Look for any registered legacy USB PHY as last resort */
if (!ci->phy && !ci->usb_phy) {
ci->usb_phy = devm_usb_get_phy(dev->parent,
USB_PHY_TYPE_USB2);
if (PTR_ERR(ci->usb_phy) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
goto ulpi_exit;
} else if (IS_ERR(ci->usb_phy)) {
ci->usb_phy = NULL;
}
}
/* No USB PHY was found in the end */
if (!ci->phy && !ci->usb_phy) {
ret = -ENXIO;
goto ulpi_exit;
}
}
ret = ci_usb_phy_init(ci);
if (ret) {
dev_err(dev, "unable to init phy: %d\n", ret);
return ret;
}
ci->hw_bank.phys = res->start;
ci->irq = platform_get_irq(pdev, 0);
if (ci->irq < 0) {
ret = ci->irq;
goto deinit_phy;
}
ci_get_otg_capable(ci);
dr_mode = ci->platdata->dr_mode;
/* initialize role(s) before the interrupt is requested */
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
ret = ci_hdrc_host_init(ci);
if (ret) {
if (ret == -ENXIO)
dev_info(dev, "doesn't support host\n");
else
goto deinit_phy;
}
}
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
ret = ci_hdrc_gadget_init(ci);
if (ret) {
if (ret == -ENXIO)
dev_info(dev, "doesn't support gadget\n");
else
goto deinit_host;
}
}
if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) {
dev_err(dev, "no supported roles\n");
ret = -ENODEV;
goto deinit_gadget;
}
if (ci->is_otg && ci->roles[CI_ROLE_GADGET]) {
ret = ci_hdrc_otg_init(ci);
if (ret) {
dev_err(dev, "init otg fails, ret = %d\n", ret);
goto deinit_gadget;
}
}
if (ci_role_switch.fwnode) {
ci_role_switch.driver_data = ci;
ci->role_switch = usb_role_switch_register(dev,
&ci_role_switch);
if (IS_ERR(ci->role_switch)) {
ret = PTR_ERR(ci->role_switch);
goto deinit_otg;
}
}
if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) {
if (ci->is_otg) {
ci->role = ci_otg_role(ci);
/* Enable ID change irq */
hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE);
} else {
/*
* If the controller is not OTG capable, but support
* role switch, the defalt role is gadget, and the
* user can switch it through debugfs.
*/
ci->role = CI_ROLE_GADGET;
}
} else {
ci->role = ci->roles[CI_ROLE_HOST]
? CI_ROLE_HOST
: CI_ROLE_GADGET;
}
if (!ci_otg_is_fsm_mode(ci)) {
/* only update vbus status for peripheral */
if (ci->role == CI_ROLE_GADGET) {
/* Pull down DP for possible charger detection */
hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
ci_handle_vbus_change(ci);
}
ret = ci_role_start(ci, ci->role);
if (ret) {
dev_err(dev, "can't start %s role\n",
ci_role(ci)->name);
goto stop;
}
}
ret = devm_request_irq(dev, ci->irq, ci_irq_handler, IRQF_SHARED,
ci->platdata->name, ci);
if (ret)
goto stop;
ret = ci_extcon_register(ci);
if (ret)
goto stop;
if (ci->supports_runtime_pm) {
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
pm_runtime_mark_last_busy(ci->dev);
pm_runtime_use_autosuspend(&pdev->dev);
}
if (ci_otg_is_fsm_mode(ci))
ci_hdrc_otg_fsm_start(ci);
device_set_wakeup_capable(&pdev->dev, true);
dbg_create_files(ci);
return 0;
stop:
if (ci->role_switch)
usb_role_switch_unregister(ci->role_switch);
deinit_otg:
if (ci->is_otg && ci->roles[CI_ROLE_GADGET])
ci_hdrc_otg_destroy(ci);
deinit_gadget:
ci_hdrc_gadget_destroy(ci);
deinit_host:
ci_hdrc_host_destroy(ci);
deinit_phy:
ci_usb_phy_exit(ci);
ulpi_exit:
ci_ulpi_exit(ci);
return ret;
}