static int ci_hdrc_probe()

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