diff options
Diffstat (limited to 'drivers/usb/dwc3/core.c')
| -rw-r--r-- | drivers/usb/dwc3/core.c | 453 | 
1 files changed, 329 insertions, 124 deletions
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 474162e9d01..eb69eb9f06c 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -61,9 +61,10 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)   * dwc3_core_soft_reset - Issues core soft reset and PHY reset   * @dwc: pointer to our context structure   */ -static void dwc3_core_soft_reset(struct dwc3 *dwc) +static int dwc3_core_soft_reset(struct dwc3 *dwc)  {  	u32		reg; +	int		ret;  	/* Before Resetting PHY, put Core in Reset */  	reg = dwc3_readl(dwc->regs, DWC3_GCTL); @@ -82,6 +83,15 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc)  	usb_phy_init(dwc->usb2_phy);  	usb_phy_init(dwc->usb3_phy); +	ret = phy_init(dwc->usb2_generic_phy); +	if (ret < 0) +		return ret; + +	ret = phy_init(dwc->usb3_generic_phy); +	if (ret < 0) { +		phy_exit(dwc->usb2_generic_phy); +		return ret; +	}  	mdelay(100);  	/* Clear USB3 PHY reset */ @@ -100,6 +110,8 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc)  	reg = dwc3_readl(dwc->regs, DWC3_GCTL);  	reg &= ~DWC3_GCTL_CORESOFTRESET;  	dwc3_writel(dwc->regs, DWC3_GCTL, reg); + +	return 0;  }  /** @@ -242,6 +254,90 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)  	}  } +static int dwc3_alloc_scratch_buffers(struct dwc3 *dwc) +{ +	if (!dwc->has_hibernation) +		return 0; + +	if (!dwc->nr_scratch) +		return 0; + +	dwc->scratchbuf = kmalloc_array(dwc->nr_scratch, +			DWC3_SCRATCHBUF_SIZE, GFP_KERNEL); +	if (!dwc->scratchbuf) +		return -ENOMEM; + +	return 0; +} + +static int dwc3_setup_scratch_buffers(struct dwc3 *dwc) +{ +	dma_addr_t scratch_addr; +	u32 param; +	int ret; + +	if (!dwc->has_hibernation) +		return 0; + +	if (!dwc->nr_scratch) +		return 0; + +	 /* should never fall here */ +	if (!WARN_ON(dwc->scratchbuf)) +		return 0; + +	scratch_addr = dma_map_single(dwc->dev, dwc->scratchbuf, +			dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE, +			DMA_BIDIRECTIONAL); +	if (dma_mapping_error(dwc->dev, scratch_addr)) { +		dev_err(dwc->dev, "failed to map scratch buffer\n"); +		ret = -EFAULT; +		goto err0; +	} + +	dwc->scratch_addr = scratch_addr; + +	param = lower_32_bits(scratch_addr); + +	ret = dwc3_send_gadget_generic_command(dwc, +			DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO, param); +	if (ret < 0) +		goto err1; + +	param = upper_32_bits(scratch_addr); + +	ret = dwc3_send_gadget_generic_command(dwc, +			DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI, param); +	if (ret < 0) +		goto err1; + +	return 0; + +err1: +	dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch * +			DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL); + +err0: +	return ret; +} + +static void dwc3_free_scratch_buffers(struct dwc3 *dwc) +{ +	if (!dwc->has_hibernation) +		return; + +	if (!dwc->nr_scratch) +		return; + +	 /* should never fall here */ +	if (!WARN_ON(dwc->scratchbuf)) +		return; + +	dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch * +			DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL); +	kfree(dwc->scratchbuf); +} +  static void dwc3_core_num_eps(struct dwc3 *dwc)  {  	struct dwc3_hwparams	*parms = &dwc->hwparams; @@ -277,6 +373,7 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)  static int dwc3_core_init(struct dwc3 *dwc)  {  	unsigned long		timeout; +	u32			hwparams4 = dwc->hwparams.hwparams4;  	u32			reg;  	int			ret; @@ -306,7 +403,9 @@ static int dwc3_core_init(struct dwc3 *dwc)  		cpu_relax();  	} while (true); -	dwc3_core_soft_reset(dwc); +	ret = dwc3_core_soft_reset(dwc); +	if (ret) +		goto err0;  	reg = dwc3_readl(dwc->regs, DWC3_GCTL);  	reg &= ~DWC3_GCTL_SCALEDOWN_MASK; @@ -314,7 +413,29 @@ static int dwc3_core_init(struct dwc3 *dwc)  	switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) {  	case DWC3_GHWPARAMS1_EN_PWROPT_CLK: -		reg &= ~DWC3_GCTL_DSBLCLKGTNG; +		/** +		 * WORKAROUND: DWC3 revisions between 2.10a and 2.50a have an +		 * issue which would cause xHCI compliance tests to fail. +		 * +		 * Because of that we cannot enable clock gating on such +		 * configurations. +		 * +		 * Refers to: +		 * +		 * STAR#9000588375: Clock Gating, SOF Issues when ref_clk-Based +		 * SOF/ITP Mode Used +		 */ +		if ((dwc->dr_mode == USB_DR_MODE_HOST || +				dwc->dr_mode == USB_DR_MODE_OTG) && +				(dwc->revision >= DWC3_REVISION_210A && +				dwc->revision <= DWC3_REVISION_250A)) +			reg |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC; +		else +			reg &= ~DWC3_GCTL_DSBLCLKGTNG; +		break; +	case DWC3_GHWPARAMS1_EN_PWROPT_HIB: +		/* enable hibernation here */ +		dwc->nr_scratch = DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(hwparams4);  		break;  	default:  		dev_dbg(dwc->dev, "No power optimization available\n"); @@ -333,16 +454,166 @@ static int dwc3_core_init(struct dwc3 *dwc)  	dwc3_writel(dwc->regs, DWC3_GCTL, reg); +	ret = dwc3_alloc_scratch_buffers(dwc); +	if (ret) +		goto err1; + +	ret = dwc3_setup_scratch_buffers(dwc); +	if (ret) +		goto err2; +  	return 0; +err2: +	dwc3_free_scratch_buffers(dwc); + +err1: +	usb_phy_shutdown(dwc->usb2_phy); +	usb_phy_shutdown(dwc->usb3_phy); +	phy_exit(dwc->usb2_generic_phy); +	phy_exit(dwc->usb3_generic_phy); +  err0:  	return ret;  }  static void dwc3_core_exit(struct dwc3 *dwc)  { +	dwc3_free_scratch_buffers(dwc);  	usb_phy_shutdown(dwc->usb2_phy);  	usb_phy_shutdown(dwc->usb3_phy); +	phy_exit(dwc->usb2_generic_phy); +	phy_exit(dwc->usb3_generic_phy); +} + +static int dwc3_core_get_phy(struct dwc3 *dwc) +{ +	struct device		*dev = dwc->dev; +	struct device_node	*node = dev->of_node; +	int ret; + +	if (node) { +		dwc->usb2_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0); +		dwc->usb3_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 1); +	} else { +		dwc->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); +		dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3); +	} + +	if (IS_ERR(dwc->usb2_phy)) { +		ret = PTR_ERR(dwc->usb2_phy); +		if (ret == -ENXIO || ret == -ENODEV) { +			dwc->usb2_phy = NULL; +		} else if (ret == -EPROBE_DEFER) { +			return ret; +		} else { +			dev_err(dev, "no usb2 phy configured\n"); +			return ret; +		} +	} + +	if (IS_ERR(dwc->usb3_phy)) { +		ret = PTR_ERR(dwc->usb3_phy); +		if (ret == -ENXIO || ret == -ENODEV) { +			dwc->usb3_phy = NULL; +		} else if (ret == -EPROBE_DEFER) { +			return ret; +		} else { +			dev_err(dev, "no usb3 phy configured\n"); +			return ret; +		} +	} + +	dwc->usb2_generic_phy = devm_phy_get(dev, "usb2-phy"); +	if (IS_ERR(dwc->usb2_generic_phy)) { +		ret = PTR_ERR(dwc->usb2_generic_phy); +		if (ret == -ENOSYS || ret == -ENODEV) { +			dwc->usb2_generic_phy = NULL; +		} else if (ret == -EPROBE_DEFER) { +			return ret; +		} else { +			dev_err(dev, "no usb2 phy configured\n"); +			return ret; +		} +	} + +	dwc->usb3_generic_phy = devm_phy_get(dev, "usb3-phy"); +	if (IS_ERR(dwc->usb3_generic_phy)) { +		ret = PTR_ERR(dwc->usb3_generic_phy); +		if (ret == -ENOSYS || ret == -ENODEV) { +			dwc->usb3_generic_phy = NULL; +		} else if (ret == -EPROBE_DEFER) { +			return ret; +		} else { +			dev_err(dev, "no usb3 phy configured\n"); +			return ret; +		} +	} + +	return 0; +} + +static int dwc3_core_init_mode(struct dwc3 *dwc) +{ +	struct device *dev = dwc->dev; +	int ret; + +	switch (dwc->dr_mode) { +	case USB_DR_MODE_PERIPHERAL: +		dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); +		ret = dwc3_gadget_init(dwc); +		if (ret) { +			dev_err(dev, "failed to initialize gadget\n"); +			return ret; +		} +		break; +	case USB_DR_MODE_HOST: +		dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST); +		ret = dwc3_host_init(dwc); +		if (ret) { +			dev_err(dev, "failed to initialize host\n"); +			return ret; +		} +		break; +	case USB_DR_MODE_OTG: +		dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG); +		ret = dwc3_host_init(dwc); +		if (ret) { +			dev_err(dev, "failed to initialize host\n"); +			return ret; +		} + +		ret = dwc3_gadget_init(dwc); +		if (ret) { +			dev_err(dev, "failed to initialize gadget\n"); +			return ret; +		} +		break; +	default: +		dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode); +		return -EINVAL; +	} + +	return 0; +} + +static void dwc3_core_exit_mode(struct dwc3 *dwc) +{ +	switch (dwc->dr_mode) { +	case USB_DR_MODE_PERIPHERAL: +		dwc3_gadget_exit(dwc); +		break; +	case USB_DR_MODE_HOST: +		dwc3_host_exit(dwc); +		break; +	case USB_DR_MODE_OTG: +		dwc3_host_exit(dwc); +		dwc3_gadget_exit(dwc); +		break; +	default: +		/* do nothing */ +		break; +	}  }  #define DWC3_ALIGN_MASK		(16 - 1) @@ -355,7 +626,7 @@ static int dwc3_probe(struct platform_device *pdev)  	struct resource		*res;  	struct dwc3		*dwc; -	int			ret = -ENOMEM; +	int			ret;  	void __iomem		*regs;  	void			*mem; @@ -367,6 +638,7 @@ static int dwc3_probe(struct platform_device *pdev)  	}  	dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1);  	dwc->mem = mem; +	dwc->dev = dev;  	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);  	if (!res) { @@ -387,57 +659,22 @@ static int dwc3_probe(struct platform_device *pdev)  	if (node) {  		dwc->maximum_speed = of_usb_get_maximum_speed(node); -		dwc->usb2_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0); -		dwc->usb3_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 1); -  		dwc->needs_fifo_resize = of_property_read_bool(node, "tx-fifo-resize");  		dwc->dr_mode = of_usb_get_dr_mode(node);  	} else if (pdata) {  		dwc->maximum_speed = pdata->maximum_speed; -		dwc->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); -		dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3); -  		dwc->needs_fifo_resize = pdata->tx_fifo_resize;  		dwc->dr_mode = pdata->dr_mode; -	} else { -		dwc->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); -		dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3);  	}  	/* default to superspeed if no maximum_speed passed */  	if (dwc->maximum_speed == USB_SPEED_UNKNOWN)  		dwc->maximum_speed = USB_SPEED_SUPER; -	if (IS_ERR(dwc->usb2_phy)) { -		ret = PTR_ERR(dwc->usb2_phy); - -		/* -		 * if -ENXIO is returned, it means PHY layer wasn't -		 * enabled, so it makes no sense to return -EPROBE_DEFER -		 * in that case, since no PHY driver will ever probe. -		 */ -		if (ret == -ENXIO) -			return ret; - -		dev_err(dev, "no usb2 phy configured\n"); -		return -EPROBE_DEFER; -	} - -	if (IS_ERR(dwc->usb3_phy)) { -		ret = PTR_ERR(dwc->usb3_phy); - -		/* -		 * if -ENXIO is returned, it means PHY layer wasn't -		 * enabled, so it makes no sense to return -EPROBE_DEFER -		 * in that case, since no PHY driver will ever probe. -		 */ -		if (ret == -ENXIO) -			return ret; - -		dev_err(dev, "no usb3 phy configured\n"); -		return -EPROBE_DEFER; -	} +	ret = dwc3_core_get_phy(dwc); +	if (ret) +		return ret;  	dwc->xhci_resources[0].start = res->start;  	dwc->xhci_resources[0].end = dwc->xhci_resources[0].start + @@ -455,15 +692,11 @@ static int dwc3_probe(struct platform_device *pdev)  	if (IS_ERR(regs))  		return PTR_ERR(regs); -	usb_phy_set_suspend(dwc->usb2_phy, 0); -	usb_phy_set_suspend(dwc->usb3_phy, 0); -  	spin_lock_init(&dwc->lock);  	platform_set_drvdata(pdev, dwc);  	dwc->regs	= regs;  	dwc->regs_size	= resource_size(res); -	dwc->dev	= dev;  	dev->dma_mask	= dev->parent->dma_mask;  	dev->dma_parms	= dev->parent->dma_parms; @@ -482,61 +715,39 @@ static int dwc3_probe(struct platform_device *pdev)  		goto err0;  	} +	if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) +		dwc->dr_mode = USB_DR_MODE_HOST; +	else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) +		dwc->dr_mode = USB_DR_MODE_PERIPHERAL; + +	if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) +		dwc->dr_mode = USB_DR_MODE_OTG; +  	ret = dwc3_core_init(dwc);  	if (ret) {  		dev_err(dev, "failed to initialize core\n");  		goto err0;  	} +	usb_phy_set_suspend(dwc->usb2_phy, 0); +	usb_phy_set_suspend(dwc->usb3_phy, 0); +	ret = phy_power_on(dwc->usb2_generic_phy); +	if (ret < 0) +		goto err1; + +	ret = phy_power_on(dwc->usb3_generic_phy); +	if (ret < 0) +		goto err_usb2phy_power; +  	ret = dwc3_event_buffers_setup(dwc);  	if (ret) {  		dev_err(dwc->dev, "failed to setup event buffers\n"); -		goto err1; +		goto err_usb3phy_power;  	} -	if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) -		dwc->dr_mode = USB_DR_MODE_HOST; -	else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) -		dwc->dr_mode = USB_DR_MODE_PERIPHERAL; - -	if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) -		dwc->dr_mode = USB_DR_MODE_OTG; - -	switch (dwc->dr_mode) { -	case USB_DR_MODE_PERIPHERAL: -		dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); -		ret = dwc3_gadget_init(dwc); -		if (ret) { -			dev_err(dev, "failed to initialize gadget\n"); -			goto err2; -		} -		break; -	case USB_DR_MODE_HOST: -		dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST); -		ret = dwc3_host_init(dwc); -		if (ret) { -			dev_err(dev, "failed to initialize host\n"); -			goto err2; -		} -		break; -	case USB_DR_MODE_OTG: -		dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG); -		ret = dwc3_host_init(dwc); -		if (ret) { -			dev_err(dev, "failed to initialize host\n"); -			goto err2; -		} - -		ret = dwc3_gadget_init(dwc); -		if (ret) { -			dev_err(dev, "failed to initialize gadget\n"); -			goto err2; -		} -		break; -	default: -		dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode); +	ret = dwc3_core_init_mode(dwc); +	if (ret)  		goto err2; -	}  	ret = dwc3_debugfs_init(dwc);  	if (ret) { @@ -549,26 +760,20 @@ static int dwc3_probe(struct platform_device *pdev)  	return 0;  err3: -	switch (dwc->dr_mode) { -	case USB_DR_MODE_PERIPHERAL: -		dwc3_gadget_exit(dwc); -		break; -	case USB_DR_MODE_HOST: -		dwc3_host_exit(dwc); -		break; -	case USB_DR_MODE_OTG: -		dwc3_host_exit(dwc); -		dwc3_gadget_exit(dwc); -		break; -	default: -		/* do nothing */ -		break; -	} +	dwc3_core_exit_mode(dwc);  err2:  	dwc3_event_buffers_cleanup(dwc); +err_usb3phy_power: +	phy_power_off(dwc->usb3_generic_phy); + +err_usb2phy_power: +	phy_power_off(dwc->usb2_generic_phy); +  err1: +	usb_phy_set_suspend(dwc->usb2_phy, 1); +	usb_phy_set_suspend(dwc->usb3_phy, 1);  	dwc3_core_exit(dwc);  err0: @@ -583,28 +788,14 @@ static int dwc3_remove(struct platform_device *pdev)  	usb_phy_set_suspend(dwc->usb2_phy, 1);  	usb_phy_set_suspend(dwc->usb3_phy, 1); +	phy_power_off(dwc->usb2_generic_phy); +	phy_power_off(dwc->usb3_generic_phy); -	pm_runtime_put(&pdev->dev); +	pm_runtime_put_sync(&pdev->dev);  	pm_runtime_disable(&pdev->dev);  	dwc3_debugfs_exit(dwc); - -	switch (dwc->dr_mode) { -	case USB_DR_MODE_PERIPHERAL: -		dwc3_gadget_exit(dwc); -		break; -	case USB_DR_MODE_HOST: -		dwc3_host_exit(dwc); -		break; -	case USB_DR_MODE_OTG: -		dwc3_host_exit(dwc); -		dwc3_gadget_exit(dwc); -		break; -	default: -		/* do nothing */ -		break; -	} - +	dwc3_core_exit_mode(dwc);  	dwc3_event_buffers_cleanup(dwc);  	dwc3_free_event_buffers(dwc);  	dwc3_core_exit(dwc); @@ -643,6 +834,7 @@ static void dwc3_complete(struct device *dev)  	spin_lock_irqsave(&dwc->lock, flags); +	dwc3_event_buffers_setup(dwc);  	switch (dwc->dr_mode) {  	case USB_DR_MODE_PERIPHERAL:  	case USB_DR_MODE_OTG: @@ -650,7 +842,6 @@ static void dwc3_complete(struct device *dev)  		/* FALLTHROUGH */  	case USB_DR_MODE_HOST:  	default: -		dwc3_event_buffers_setup(dwc);  		break;  	} @@ -680,6 +871,8 @@ static int dwc3_suspend(struct device *dev)  	usb_phy_shutdown(dwc->usb3_phy);  	usb_phy_shutdown(dwc->usb2_phy); +	phy_exit(dwc->usb2_generic_phy); +	phy_exit(dwc->usb3_generic_phy);  	return 0;  } @@ -688,10 +881,17 @@ static int dwc3_resume(struct device *dev)  {  	struct dwc3	*dwc = dev_get_drvdata(dev);  	unsigned long	flags; +	int		ret;  	usb_phy_init(dwc->usb3_phy);  	usb_phy_init(dwc->usb2_phy); -	msleep(100); +	ret = phy_init(dwc->usb2_generic_phy); +	if (ret < 0) +		return ret; + +	ret = phy_init(dwc->usb3_generic_phy); +	if (ret < 0) +		goto err_usb2phy_init;  	spin_lock_irqsave(&dwc->lock, flags); @@ -715,6 +915,11 @@ static int dwc3_resume(struct device *dev)  	pm_runtime_enable(dev);  	return 0; + +err_usb2phy_init: +	phy_exit(dwc->usb2_generic_phy); + +	return ret;  }  static const struct dev_pm_ops dwc3_dev_pm_ops = {  | 
