Exemple #1
0
static int exynos_xhci_runtime_resume(struct device *dev)
{
    struct platform_device	*pdev = to_platform_device(dev);
    struct exynos_xhci_hcd	*exynos_xhci;
    struct usb_hcd		*hcd;
    struct xhci_hcd		*xhci;
    int			retval = 0;

    dev_dbg(dev, "%s\n", __func__);

    exynos_xhci = dev_get_drvdata(dev);
    if (!exynos_xhci)
        return -EINVAL;

    hcd = exynos_xhci->hcd;
    if (!hcd)
        return -EINVAL;

    if (dev->power.is_suspended) {
        dev_dbg(dev, "xhci is system suspended\n");
        return 0;
    }

    /* Userspace may try to access host when DRD is in B-Dev mode */
    if (exynos_drd_try_get(pdev)) {
        dev_dbg(dev, "%s: cannot get DRD\n", __func__);
        return -EAGAIN;
    }

    pm_runtime_get_sync(exynos_xhci->dev->parent);

    /*
     * Parent device (DRD core) resumes before its child (xHCI).
     * Since we "get" DRD when it's already active, we need to
     * reconfigure PHY here, so PHY tuning took effect.
     */
    if (exynos_xhci->core->ops->phy_set)
        exynos_xhci->core->ops->phy_set(exynos_xhci->core);

    if (exynos_xhci->core->ops->change_mode)
        exynos_xhci->core->ops->change_mode(exynos_xhci->core, true);

    if (exynos_xhci->core->ops->core_init)
        exynos_xhci->core->ops->core_init(exynos_xhci->core);

    xhci = hcd_to_xhci(hcd);
    retval = xhci_resume(xhci, 0);
    if (retval < 0)
        dev_err(dev, "%s: cannot start xHC\n", __func__);

    /*
     * In xhci_resume(), config values(AHB bus and los_bias) are intialized.
     * So after called xhci_resume(), set the config values again.
     */
    if (exynos_xhci->core->ops->config)
        exynos_xhci->core->ops->config(exynos_xhci->core);

    return retval;
}
static int exynos_xhci_resume(struct device *dev)
{
	struct platform_device	*pdev = to_platform_device(dev);
	struct exynos_xhci_hcd	*exynos_xhci;
	struct usb_hcd		*hcd;
	struct xhci_hcd		*xhci;
	int			retval = 0;

#ifdef CONFIG_PM_RUNTIME
	dev_dbg(dev, "%s: usage_count = %d\n",
		      __func__, atomic_read(&dev->power.usage_count));
#endif
	exynos_xhci = dev_get_drvdata(dev);
	if (!exynos_xhci)
		return -EINVAL;

	hcd = exynos_xhci->hcd;
	if (!hcd)
		return -EINVAL;

	if (exynos_drd_try_get(pdev)) {
		dev_err(dev, "%s: cannot get DRD\n", __func__);
		return -EAGAIN;
	}

	/* Wake up and initialize DRD core */
	pm_runtime_get_sync(dev->parent);
	if (exynos_xhci->core->ops->change_mode)
		exynos_xhci->core->ops->change_mode(exynos_xhci->core, true);
	if (exynos_xhci->core->ops->core_init)
		exynos_xhci->core->ops->core_init(exynos_xhci->core);

	xhci = hcd_to_xhci(hcd);
	retval = xhci_resume(xhci, 0);
	if (retval < 0)
		dev_err(dev, "%s: cannot start xHC\n", __func__);

	/*
	 * In xhci_resume(), config values(AHB bus and los_bias) are intialized.
	 * So after called xhci_resume(), set the config values again.
	 */
	if (exynos_xhci->core->ops->config)
		exynos_xhci->core->ops->config(exynos_xhci->core);

	/* Update runtime PM status and clear runtime_error */
	pm_runtime_disable(dev);
	pm_runtime_set_active(dev);
	pm_runtime_enable(dev);

	return retval;
}
static int __devinit exynos_xhci_probe(struct platform_device *pdev)
{
	struct dwc3_exynos_data	*pdata = pdev->dev.platform_data;
	struct device		*dev = &pdev->dev;
	const struct hc_driver	*driver = &exynos_xhci_hc_driver;
	struct exynos_xhci_hcd	*exynos_xhci;
	struct usb_hcd		*hcd;
	struct xhci_hcd		*xhci;
	struct resource		*res;
	int			irq;
	int			err;

	if (usb_disabled())
		return -ENODEV;

	if (!pdata) {
		dev_err(dev, "No platform data defined\n");
		return -ENODEV;
	}

	exynos_xhci = devm_kzalloc(dev, sizeof(struct exynos_xhci_hcd),
				   GFP_KERNEL);
	if (!exynos_xhci) {
		dev_err(dev, "Not enough memory\n");
		return -ENOMEM;
	}

	exynos_xhci->dev = dev;
	exynos_xhci->pdata = pdata;
	exynos_xhci->core = exynos_drd_bind(pdev);

	irq = platform_get_irq(pdev, 0);
	if (irq < 0) {
		dev_err(dev, "Failed to get IRQ\n");
		return -ENXIO;
	}

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res) {
		dev_err(dev, "Failed to get I/O memory\n");
		return -ENXIO;
	}

	/* Create and add primary HCD */

	hcd = usb_create_hcd(driver, dev, dev_name(dev));
	if (!hcd) {
		dev_err(dev, "Failed to create primary HCD\n");
		return -ENOMEM;
	}

	exynos_xhci->hcd = hcd;
	/* Rewrite driver data with our structure */
	platform_set_drvdata(pdev, exynos_xhci);

	hcd->rsrc_start = res->start;
	hcd->rsrc_len = resource_size(res);

	if (!devm_request_mem_region(dev, res->start,
				     resource_size(res), dev_name(dev))) {
		dev_err(dev, "Failed to reserve registers\n");
		err = -ENOENT;
		goto put_hcd;
	}

	hcd->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
	if (!hcd->regs) {
		dev_err(dev, "Failed to remap I/O memory\n");
		err = -ENOMEM;
		goto put_hcd;
	}
	hcd->regs -= EXYNOS_USB3_XHCI_REG_START;

	err = exynos_drd_try_get(pdev);
	if (err) {
		/* REVISIT: what shall we do if UDC is already running */
		dev_err(dev, "Failed to access DRD\n");
		goto put_hcd;
	}

	/* Wake up and initialize DRD core */
	pm_runtime_get_sync(dev->parent);
	if (exynos_xhci->core->ops->change_mode)
		exynos_xhci->core->ops->change_mode(exynos_xhci->core, true);
	if (exynos_xhci->core->ops->core_init)
		exynos_xhci->core->ops->core_init(exynos_xhci->core);

	err = usb_add_hcd(hcd, irq, IRQF_SHARED);
	if (err) {
		dev_err(dev, "Failed to add primary HCD\n");
		goto put_hcd;
	}

	/* Create and add shared HCD */

	xhci = hcd_to_xhci(hcd);
	exynos_xhci_dbg = xhci;

	xhci->shared_hcd = usb_create_shared_hcd(driver, dev,
						 dev_name(dev), hcd);
	if (!xhci->shared_hcd) {
		dev_err(dev, "Failed to create shared HCD\n");
		err = -ENOMEM;
		goto remove_hcd;
	}

	xhci->shared_hcd->regs = hcd->regs;

	/*
	 * Set the xHCI pointer before exynos_xhci_setup()
	 * (aka hcd_driver.reset) is called by usb_add_hcd().
	 */
	*((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci;

	err = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
	if (err) {
		dev_err(dev, "Failed to add shared HCD\n");
		goto put_usb3_hcd;
	}

	pm_runtime_set_active(dev);
	pm_runtime_enable(dev);

	if (exynos_xhci->core->otg) {
		err = otg_set_host(exynos_xhci->core->otg, &hcd->self);
		if (err) {
			dev_err(dev, "Unable to bind hcd to DRD switch\n");
			goto remove_usb3_hcd;
		}
	}

	return 0;

remove_usb3_hcd:
	pm_runtime_disable(dev);
	usb_remove_hcd(xhci->shared_hcd);
put_usb3_hcd:
	usb_put_hcd(xhci->shared_hcd);
remove_hcd:
	usb_remove_hcd(hcd);
put_hcd:
	usb_put_hcd(hcd);

	return err;
}