diff options
Diffstat (limited to 'drivers/usb/dwc3/core.c')
-rw-r--r-- | drivers/usb/dwc3/core.c | 251 |
1 files changed, 197 insertions, 54 deletions
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index b0f4d52b7f04..25ddc39efad8 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -19,6 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <linux/version.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/slab.h> @@ -32,6 +33,7 @@ #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/of.h> +#include <linux/acpi.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> @@ -363,6 +365,72 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc) } /** + * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core + * @dwc: Pointer to our controller context structure + */ +static void dwc3_phy_setup(struct dwc3 *dwc) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); + + /* + * Above 1.94a, it is recommended to set DWC3_GUSB3PIPECTL_SUSPHY + * to '0' during coreConsultant configuration. So default value + * will be '0' when the core is reset. Application needs to set it + * to '1' after the core initialization is completed. + */ + if (dwc->revision > DWC3_REVISION_194A) + reg |= DWC3_GUSB3PIPECTL_SUSPHY; + + if (dwc->u2ss_inp3_quirk) + reg |= DWC3_GUSB3PIPECTL_U2SSINP3OK; + + if (dwc->req_p1p2p3_quirk) + reg |= DWC3_GUSB3PIPECTL_REQP1P2P3; + + if (dwc->del_p1p2p3_quirk) + reg |= DWC3_GUSB3PIPECTL_DEP1P2P3_EN; + + if (dwc->del_phy_power_chg_quirk) + reg |= DWC3_GUSB3PIPECTL_DEPOCHANGE; + + if (dwc->lfps_filter_quirk) + reg |= DWC3_GUSB3PIPECTL_LFPSFILT; + + if (dwc->rx_detect_poll_quirk) + reg |= DWC3_GUSB3PIPECTL_RX_DETOPOLL; + + if (dwc->tx_de_emphasis_quirk) + reg |= DWC3_GUSB3PIPECTL_TX_DEEPH(dwc->tx_de_emphasis); + + if (dwc->dis_u3_susphy_quirk) + reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; + + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + + mdelay(100); + + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + + /* + * Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to + * '0' during coreConsultant configuration. So default value will + * be '0' when the core is reset. Application needs to set it to + * '1' after the core initialization is completed. + */ + if (dwc->revision > DWC3_REVISION_194A) + reg |= DWC3_GUSB2PHYCFG_SUSPHY; + + if (dwc->dis_u2_susphy_quirk) + reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; + + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + + mdelay(100); +} + +/** * dwc3_core_init - Low-level initialization of DWC3 Core * @dwc: Pointer to our controller context structure * @@ -384,6 +452,12 @@ static int dwc3_core_init(struct dwc3 *dwc) } dwc->revision = reg; + /* + * Write Linux Version Code to our GUID register so it's easy to figure + * out which kernel version a bug was found. + */ + dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE); + /* Handle USB2.0-only core configuration */ if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == DWC3_GHWPARAMS3_SSPHY_IFC_DIS) { @@ -414,7 +488,6 @@ static int dwc3_core_init(struct dwc3 *dwc) reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg &= ~DWC3_GCTL_SCALEDOWN_MASK; - reg &= ~DWC3_GCTL_DISSCRAMBLE; switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) { case DWC3_GHWPARAMS1_EN_PWROPT_CLK: @@ -441,11 +514,34 @@ static int dwc3_core_init(struct dwc3 *dwc) case DWC3_GHWPARAMS1_EN_PWROPT_HIB: /* enable hibernation here */ dwc->nr_scratch = DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(hwparams4); + + /* + * REVISIT Enabling this bit so that host-mode hibernation + * will work. Device-mode hibernation is not yet implemented. + */ + reg |= DWC3_GCTL_GBLHIBERNATIONEN; break; default: dev_dbg(dwc->dev, "No power optimization available\n"); } + /* check if current dwc3 is on simulation board */ + if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) { + dev_dbg(dwc->dev, "it is on FPGA board\n"); + dwc->is_fpga = true; + } + + WARN_ONCE(dwc->disable_scramble_quirk && !dwc->is_fpga, + "disable_scramble cannot be used on non-FPGA builds\n"); + + if (dwc->disable_scramble_quirk && dwc->is_fpga) + reg |= DWC3_GCTL_DISSCRAMBLE; + else + reg &= ~DWC3_GCTL_DISSCRAMBLE; + + if (dwc->u2exit_lfps_quirk) + reg |= DWC3_GCTL_U2EXIT_LFPS; + /* * WORKAROUND: DWC3 revisions <1.90a have a bug * where the device can fail to connect at SuperSpeed @@ -459,6 +555,8 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GCTL, reg); + dwc3_phy_setup(dwc); + ret = dwc3_alloc_scratch_buffers(dwc); if (ret) goto err1; @@ -630,6 +728,9 @@ static int dwc3_probe(struct platform_device *pdev) struct device_node *node = dev->of_node; struct resource *res; struct dwc3 *dwc; + u8 lpm_nyet_threshold; + u8 tx_de_emphasis; + u8 hird_threshold; int ret; @@ -685,22 +786,96 @@ static int dwc3_probe(struct platform_device *pdev) */ res->start -= DWC3_GLOBALS_REGS_START; + /* default to highest possible threshold */ + lpm_nyet_threshold = 0xff; + + /* default to -3.5dB de-emphasis */ + tx_de_emphasis = 1; + + /* + * default to assert utmi_sleep_n and use maximum allowed HIRD + * threshold value of 0b1100 + */ + hird_threshold = 12; + if (node) { dwc->maximum_speed = of_usb_get_maximum_speed(node); - - dwc->needs_fifo_resize = of_property_read_bool(node, "tx-fifo-resize"); + dwc->has_lpm_erratum = of_property_read_bool(node, + "snps,has-lpm-erratum"); + of_property_read_u8(node, "snps,lpm-nyet-threshold", + &lpm_nyet_threshold); + dwc->is_utmi_l1_suspend = of_property_read_bool(node, + "snps,is-utmi-l1-suspend"); + of_property_read_u8(node, "snps,hird-threshold", + &hird_threshold); + + dwc->needs_fifo_resize = of_property_read_bool(node, + "tx-fifo-resize"); dwc->dr_mode = of_usb_get_dr_mode(node); + + dwc->disable_scramble_quirk = of_property_read_bool(node, + "snps,disable_scramble_quirk"); + dwc->u2exit_lfps_quirk = of_property_read_bool(node, + "snps,u2exit_lfps_quirk"); + dwc->u2ss_inp3_quirk = of_property_read_bool(node, + "snps,u2ss_inp3_quirk"); + dwc->req_p1p2p3_quirk = of_property_read_bool(node, + "snps,req_p1p2p3_quirk"); + dwc->del_p1p2p3_quirk = of_property_read_bool(node, + "snps,del_p1p2p3_quirk"); + dwc->del_phy_power_chg_quirk = of_property_read_bool(node, + "snps,del_phy_power_chg_quirk"); + dwc->lfps_filter_quirk = of_property_read_bool(node, + "snps,lfps_filter_quirk"); + dwc->rx_detect_poll_quirk = of_property_read_bool(node, + "snps,rx_detect_poll_quirk"); + dwc->dis_u3_susphy_quirk = of_property_read_bool(node, + "snps,dis_u3_susphy_quirk"); + dwc->dis_u2_susphy_quirk = of_property_read_bool(node, + "snps,dis_u2_susphy_quirk"); + + dwc->tx_de_emphasis_quirk = of_property_read_bool(node, + "snps,tx_de_emphasis_quirk"); + of_property_read_u8(node, "snps,tx_de_emphasis", + &tx_de_emphasis); } else if (pdata) { dwc->maximum_speed = pdata->maximum_speed; + dwc->has_lpm_erratum = pdata->has_lpm_erratum; + if (pdata->lpm_nyet_threshold) + lpm_nyet_threshold = pdata->lpm_nyet_threshold; + dwc->is_utmi_l1_suspend = pdata->is_utmi_l1_suspend; + if (pdata->hird_threshold) + hird_threshold = pdata->hird_threshold; dwc->needs_fifo_resize = pdata->tx_fifo_resize; dwc->dr_mode = pdata->dr_mode; + + dwc->disable_scramble_quirk = pdata->disable_scramble_quirk; + dwc->u2exit_lfps_quirk = pdata->u2exit_lfps_quirk; + dwc->u2ss_inp3_quirk = pdata->u2ss_inp3_quirk; + dwc->req_p1p2p3_quirk = pdata->req_p1p2p3_quirk; + dwc->del_p1p2p3_quirk = pdata->del_p1p2p3_quirk; + dwc->del_phy_power_chg_quirk = pdata->del_phy_power_chg_quirk; + dwc->lfps_filter_quirk = pdata->lfps_filter_quirk; + dwc->rx_detect_poll_quirk = pdata->rx_detect_poll_quirk; + dwc->dis_u3_susphy_quirk = pdata->dis_u3_susphy_quirk; + dwc->dis_u2_susphy_quirk = pdata->dis_u2_susphy_quirk; + + dwc->tx_de_emphasis_quirk = pdata->tx_de_emphasis_quirk; + if (pdata->tx_de_emphasis) + tx_de_emphasis = pdata->tx_de_emphasis; } /* default to superspeed if no maximum_speed passed */ if (dwc->maximum_speed == USB_SPEED_UNKNOWN) dwc->maximum_speed = USB_SPEED_SUPER; + dwc->lpm_nyet_threshold = lpm_nyet_threshold; + dwc->tx_de_emphasis = tx_de_emphasis; + + dwc->hird_threshold = hird_threshold + | (dwc->is_utmi_l1_suspend << 4); + ret = dwc3_core_get_phy(dwc); if (ret) return ret; @@ -708,9 +883,11 @@ static int dwc3_probe(struct platform_device *pdev) spin_lock_init(&dwc->lock); platform_set_drvdata(pdev, dwc); - dev->dma_mask = dev->parent->dma_mask; - dev->dma_parms = dev->parent->dma_parms; - dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask); + if (!dev->dma_mask) { + dev->dma_mask = dev->parent->dma_mask; + dev->dma_parms = dev->parent->dma_parms; + dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask); + } pm_runtime_enable(dev); pm_runtime_get_sync(dev); @@ -815,50 +992,6 @@ static int dwc3_remove(struct platform_device *pdev) } #ifdef CONFIG_PM_SLEEP -static int dwc3_prepare(struct device *dev) -{ - struct dwc3 *dwc = dev_get_drvdata(dev); - unsigned long flags; - - spin_lock_irqsave(&dwc->lock, flags); - - switch (dwc->dr_mode) { - case USB_DR_MODE_PERIPHERAL: - case USB_DR_MODE_OTG: - dwc3_gadget_prepare(dwc); - /* FALLTHROUGH */ - case USB_DR_MODE_HOST: - default: - dwc3_event_buffers_cleanup(dwc); - break; - } - - spin_unlock_irqrestore(&dwc->lock, flags); - - return 0; -} - -static void dwc3_complete(struct device *dev) -{ - struct dwc3 *dwc = dev_get_drvdata(dev); - unsigned long flags; - - 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: - dwc3_gadget_complete(dwc); - /* FALLTHROUGH */ - case USB_DR_MODE_HOST: - default: - break; - } - - spin_unlock_irqrestore(&dwc->lock, flags); -} - static int dwc3_suspend(struct device *dev) { struct dwc3 *dwc = dev_get_drvdata(dev); @@ -873,7 +1006,7 @@ static int dwc3_suspend(struct device *dev) /* FALLTHROUGH */ case USB_DR_MODE_HOST: default: - /* do nothing */ + dwc3_event_buffers_cleanup(dwc); break; } @@ -906,6 +1039,7 @@ static int dwc3_resume(struct device *dev) spin_lock_irqsave(&dwc->lock, flags); + dwc3_event_buffers_setup(dwc); dwc3_writel(dwc->regs, DWC3_GCTL, dwc->gctl); switch (dwc->dr_mode) { @@ -934,9 +1068,6 @@ err_usb2phy_init: } static const struct dev_pm_ops dwc3_dev_pm_ops = { - .prepare = dwc3_prepare, - .complete = dwc3_complete, - SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume) }; @@ -958,12 +1089,24 @@ static const struct of_device_id of_dwc3_match[] = { MODULE_DEVICE_TABLE(of, of_dwc3_match); #endif +#ifdef CONFIG_ACPI + +#define ACPI_ID_INTEL_BSW "808622B7" + +static const struct acpi_device_id dwc3_acpi_match[] = { + { ACPI_ID_INTEL_BSW, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, dwc3_acpi_match); +#endif + static struct platform_driver dwc3_driver = { .probe = dwc3_probe, .remove = dwc3_remove, .driver = { .name = "dwc3", .of_match_table = of_match_ptr(of_dwc3_match), + .acpi_match_table = ACPI_PTR(dwc3_acpi_match), .pm = DWC3_PM_OPS, }, }; |