diff options
Diffstat (limited to 'drivers/usb/dwc3/core.c')
| -rw-r--r-- | drivers/usb/dwc3/core.c | 530 |
1 files changed, 392 insertions, 138 deletions
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index cb82557678dd..ec8407972b9d 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -25,7 +25,9 @@ #include <linux/of.h> #include <linux/of_graph.h> #include <linux/acpi.h> +#include <linux/pci.h> #include <linux/pinctrl/consumer.h> +#include <linux/pinctrl/devinfo.h> #include <linux/reset.h> #include <linux/bitfield.h> @@ -36,6 +38,7 @@ #include "core.h" #include "gadget.h" +#include "glue.h" #include "io.h" #include "debug.h" @@ -108,35 +111,56 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc) void dwc3_enable_susphy(struct dwc3 *dwc, bool enable) { u32 reg; + int i; - reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); - if (enable && !dwc->dis_u3_susphy_quirk) - reg |= DWC3_GUSB3PIPECTL_SUSPHY; - else - reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; + for (i = 0; i < dwc->num_usb3_ports; i++) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(i)); + if (enable && !dwc->dis_u3_susphy_quirk) + reg |= DWC3_GUSB3PIPECTL_SUSPHY; + else + reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; - dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(i), reg); + } - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); - if (enable && !dwc->dis_u2_susphy_quirk) - reg |= DWC3_GUSB2PHYCFG_SUSPHY; - else - reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; + for (i = 0; i < dwc->num_usb2_ports; i++) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i)); + if (enable && !dwc->dis_u2_susphy_quirk) + reg |= DWC3_GUSB2PHYCFG_SUSPHY; + else + reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg); + } } +EXPORT_SYMBOL_GPL(dwc3_enable_susphy); -void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode) +void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy) { + unsigned int hw_mode; u32 reg; reg = dwc3_readl(dwc->regs, DWC3_GCTL); + + /* + * For DRD controllers, GUSB3PIPECTL.SUSPENDENABLE and + * GUSB2PHYCFG.SUSPHY should be cleared during mode switching, + * and they can be set after core initialization. + */ + hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0); + if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD && !ignore_susphy) { + if (DWC3_GCTL_PRTCAP(reg) != mode) + dwc3_enable_susphy(dwc, false); + } + reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG)); reg |= DWC3_GCTL_PRTCAPDIR(mode); dwc3_writel(dwc->regs, DWC3_GCTL, reg); dwc->current_dr_role = mode; + trace_dwc3_set_prtcap(mode); } +EXPORT_SYMBOL_GPL(dwc3_set_prtcap); static void __dwc3_set_mode(struct work_struct *work) { @@ -211,7 +235,7 @@ static void __dwc3_set_mode(struct work_struct *work) spin_lock_irqsave(&dwc->lock, flags); - dwc3_set_prtcap(dwc, desired_dr_role); + dwc3_set_prtcap(dwc, desired_dr_role, false); spin_unlock_irqrestore(&dwc->lock, flags); @@ -259,7 +283,6 @@ static void __dwc3_set_mode(struct work_struct *work) } out: - pm_runtime_mark_last_busy(dwc->dev); pm_runtime_put_autosuspend(dwc->dev); mutex_unlock(&dwc->mutex); } @@ -539,6 +562,7 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned int length) int dwc3_event_buffers_setup(struct dwc3 *dwc) { struct dwc3_event_buffer *evt; + u32 reg; if (!dwc->ev_buf) return 0; @@ -551,17 +575,27 @@ int dwc3_event_buffers_setup(struct dwc3 *dwc) upper_32_bits(evt->dma)); dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_SIZE(evt->length)); - dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 0); + /* Clear any stale event */ + reg = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), reg); return 0; } void dwc3_event_buffers_cleanup(struct dwc3 *dwc) { struct dwc3_event_buffer *evt; + u32 reg; if (!dwc->ev_buf) return; + /* + * Exynos platforms may not be able to access event buffer if the + * controller failed to halt on dwc3_core_exit(). + */ + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + if (!(reg & DWC3_DSTS_DEVCTRLHLT)) + return; evt = dwc->ev_buf; @@ -571,7 +605,10 @@ void dwc3_event_buffers_cleanup(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0), 0); dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_INTMASK | DWC3_GEVNTSIZ_SIZE(0)); - dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 0); + + /* Clear any stale event */ + reg = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), reg); } static void dwc3_core_num_eps(struct dwc3 *dwc) @@ -599,6 +636,18 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc) parms->hwparams9 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS9); } +static void dwc3_config_soc_bus(struct dwc3 *dwc) +{ + if (dwc->gsbuscfg0_reqinfo != DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED) { + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0); + reg &= ~DWC3_GSBUSCFG0_REQINFO(~0); + reg |= DWC3_GSBUSCFG0_REQINFO(dwc->gsbuscfg0_reqinfo); + dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, reg); + } +} + static int dwc3_core_ulpi_init(struct dwc3 *dwc) { int intf; @@ -627,16 +676,7 @@ static int dwc3_ss_phy_setup(struct dwc3 *dwc, int index) */ reg &= ~DWC3_GUSB3PIPECTL_UX_EXIT_PX; - /* - * Above DWC_usb3.0 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. - * - * Similarly for DRD controllers, GUSB3PIPECTL.SUSPENDENABLE must be - * cleared after power-on reset, and it can be set after core - * initialization. - */ + /* Ensure the GUSB3PIPECTL.SUSPENDENABLE is cleared prior to phy init. */ reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; if (dwc->u2ss_inp3_quirk) @@ -716,15 +756,7 @@ static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index) break; } - /* - * Above DWC_usb3.0 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. - * - * Similarly for DRD controllers, GUSB2PHYCFG.SUSPHY must be cleared - * after power-on reset, and it can be set after core initialization. - */ + /* Ensure the GUSB2PHYCFG.SUSPHY is cleared prior to phy init. */ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; if (dwc->dis_enblslpm_quirk) @@ -799,6 +831,25 @@ static int dwc3_phy_init(struct dwc3 *dwc) goto err_exit_usb3_phy; } + /* + * Above DWC_usb3.0 1.94a, it is recommended to set + * DWC3_GUSB3PIPECTL_SUSPHY and 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. + * + * Certain phy requires to be in P0 power state during initialization. + * Make sure GUSB3PIPECTL.SUSPENDENABLE and GUSB2PHYCFG.SUSPHY are clear + * prior to phy init to maintain in the P0 state. + * + * After phy initialization, some phy operations can only be executed + * while in lower P states. Ensure GUSB3PIPECTL.SUSPENDENABLE and + * GUSB2PHYCFG.SUSPHY are set soon after initialization to avoid + * blocking phy ops. + */ + if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A)) + dwc3_enable_susphy(dwc, true); + return 0; err_exit_usb3_phy: @@ -926,7 +977,7 @@ static void dwc3_clk_disable(struct dwc3 *dwc) clk_disable_unprepare(dwc->bus_clk); } -static void dwc3_core_exit(struct dwc3 *dwc) +void dwc3_core_exit(struct dwc3 *dwc) { dwc3_event_buffers_cleanup(dwc); dwc3_phy_power_off(dwc); @@ -934,6 +985,7 @@ static void dwc3_core_exit(struct dwc3 *dwc) dwc3_clk_disable(dwc); reset_control_assert(dwc->reset); } +EXPORT_SYMBOL_GPL(dwc3_core_exit); static bool dwc3_core_is_valid(struct dwc3 *dwc) { @@ -1279,7 +1331,7 @@ static void dwc3_config_threshold(struct dwc3 *dwc) * * Returns 0 on success otherwise negative errno. */ -static int dwc3_core_init(struct dwc3 *dwc) +int dwc3_core_init(struct dwc3 *dwc) { unsigned int hw_mode; u32 reg; @@ -1338,6 +1390,8 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc3_set_incr_burst_type(dwc); + dwc3_config_soc_bus(dwc); + ret = dwc3_phy_power_on(dwc); if (ret) goto err_exit_phy; @@ -1360,8 +1414,23 @@ static int dwc3_core_init(struct dwc3 *dwc) } /* + * STAR 9001285599: This issue affects DWC_usb3 version 3.20a + * only. If the PM TIMER ECM is enabled through GUCTL2[19], the + * link compliance test (TD7.21) may fail. If the ECN is not + * enabled (GUCTL2[19] = 0), the controller will use the old timer + * value (5us), which is still acceptable for the link compliance + * test. Therefore, do not enable PM TIMER ECM in 3.20a by + * setting GUCTL2[19] by default; instead, use GUCTL2[19] = 0. + */ + if (DWC3_VER_IS(DWC3, 320A)) { + reg = dwc3_readl(dwc->regs, DWC3_GUCTL2); + reg &= ~DWC3_GUCTL2_LC_TIMER; + dwc3_writel(dwc->regs, DWC3_GUCTL2, reg); + } + + /* * When configured in HOST mode, after issuing U3/L2 exit controller - * fails to send proper CRC checksum in CRC5 feild. Because of this + * fails to send proper CRC checksum in CRC5 field. Because of this * behaviour Transaction Error is generated, resulting in reset and * re-enumeration of usb device attached. All the termsel, xcvrsel, * opmode becomes 0 during end of resume. Enabling bit 10 of GUCTL1 @@ -1415,16 +1484,36 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc3_config_threshold(dwc); - /* - * Modify this for all supported Super Speed ports when - * multiport support is added. - */ if (hw_mode != DWC3_GHWPARAMS0_MODE_GADGET && (DWC3_IP_IS(DWC31)) && dwc->maximum_speed == USB_SPEED_SUPER) { - reg = dwc3_readl(dwc->regs, DWC3_LLUCTL); - reg |= DWC3_LLUCTL_FORCE_GEN1; - dwc3_writel(dwc->regs, DWC3_LLUCTL, reg); + int i; + + for (i = 0; i < dwc->num_usb3_ports; i++) { + reg = dwc3_readl(dwc->regs, DWC3_LLUCTL(i)); + reg |= DWC3_LLUCTL_FORCE_GEN1; + dwc3_writel(dwc->regs, DWC3_LLUCTL(i), reg); + } + } + + /* + * STAR 9001346572: This issue affects DWC_usb31 versions 1.80a and + * prior. When an active endpoint not currently cached in the host + * controller is chosen to be cached to the same index as an endpoint + * receiving NAKs, the endpoint receiving NAKs enters continuous + * retry mode. This prevents it from being evicted from the host + * controller cache, blocking the new endpoint from being cached and + * serviced. + * + * To resolve this, for controller versions 1.70a and 1.80a, set the + * GUCTL3 bit[16] (USB2.0 Internal Retry Disable) to 1. This bit + * disables the USB2.0 internal retry feature. The GUCTL3[16] register + * function is available only from version 1.70a. + */ + if (DWC3_VER_IS_WITHIN(DWC31, 170A, 180A)) { + reg = dwc3_readl(dwc->regs, DWC3_GUCTL3); + reg |= DWC3_GUCTL3_USB20_RETRY_DISABLE; + dwc3_writel(dwc->regs, DWC3_GUCTL3, reg); } return 0; @@ -1438,6 +1527,7 @@ err_exit_ulpi: return ret; } +EXPORT_SYMBOL_GPL(dwc3_core_init); static int dwc3_core_get_phy(struct dwc3 *dwc) { @@ -1516,7 +1606,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) switch (dwc->dr_mode) { case USB_DR_MODE_PERIPHERAL: - dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE); + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, false); if (dwc->usb2_phy) otg_set_vbus(dwc->usb2_phy->otg, false); @@ -1528,7 +1618,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) return dev_err_probe(dev, ret, "failed to initialize gadget\n"); break; case USB_DR_MODE_HOST: - dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST); + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST, false); if (dwc->usb2_phy) otg_set_vbus(dwc->usb2_phy->otg, true); @@ -1573,7 +1663,35 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc) } /* de-assert DRVVBUS for HOST and OTG mode */ - dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE); + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, true); +} + +static void dwc3_get_software_properties(struct dwc3 *dwc, + const struct dwc3_properties *properties) +{ + struct device *tmpdev; + u16 gsbuscfg0_reqinfo; + int ret; + + dwc->gsbuscfg0_reqinfo = DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED; + + if (properties->gsbuscfg0_reqinfo != + DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED) { + dwc->gsbuscfg0_reqinfo = properties->gsbuscfg0_reqinfo; + return; + } + + /* + * Iterate over all parent nodes for finding swnode properties + * and non-DT (non-ABI) properties. + */ + for (tmpdev = dwc->dev; tmpdev; tmpdev = tmpdev->parent) { + ret = device_property_read_u16(tmpdev, + "snps,gsbuscfg0-reqinfo", + &gsbuscfg0_reqinfo); + if (!ret) + dwc->gsbuscfg0_reqinfo = gsbuscfg0_reqinfo; + } } static void dwc3_get_properties(struct dwc3 *dwc) @@ -1591,8 +1709,7 @@ static void dwc3_get_properties(struct dwc3 *dwc) u8 tx_thr_num_pkt_prd = 0; u8 tx_max_burst_prd = 0; u8 tx_fifo_resize_max_num; - const char *usb_psy_name; - int ret; + u16 num_hc_interrupters; /* default to highest possible threshold */ lpm_nyet_threshold = 0xf; @@ -1613,6 +1730,9 @@ static void dwc3_get_properties(struct dwc3 *dwc) */ tx_fifo_resize_max_num = 6; + /* default to a single XHCI interrupter */ + num_hc_interrupters = 1; + dwc->maximum_speed = usb_get_maximum_speed(dev); dwc->max_ssp_rate = usb_get_maximum_ssp_rate(dev); dwc->dr_mode = usb_get_dr_mode(dev); @@ -1627,13 +1747,6 @@ static void dwc3_get_properties(struct dwc3 *dwc) dwc->sys_wakeup = device_may_wakeup(dwc->sysdev); - ret = device_property_read_string(dev, "usb-psy-name", &usb_psy_name); - if (ret >= 0) { - dwc->usb_psy = power_supply_get_by_name(usb_psy_name); - if (!dwc->usb_psy) - dev_err(dev, "couldn't get usb power supply\n"); - } - dwc->has_lpm_erratum = device_property_read_bool(dev, "snps,has-lpm-erratum"); device_property_read_u8(dev, "snps,lpm-nyet-threshold", @@ -1666,6 +1779,12 @@ static void dwc3_get_properties(struct dwc3 *dwc) &tx_thr_num_pkt_prd); device_property_read_u8(dev, "snps,tx-max-burst-prd", &tx_max_burst_prd); + device_property_read_u16(dev, "num-hc-interrupters", + &num_hc_interrupters); + /* DWC3 core allowed to have a max of 8 interrupters */ + if (num_hc_interrupters > 8) + num_hc_interrupters = 8; + dwc->do_fifo_resize = device_property_read_bool(dev, "tx-fifo-resize"); if (dwc->do_fifo_resize) @@ -1751,9 +1870,9 @@ static void dwc3_get_properties(struct dwc3 *dwc) dwc->tx_thr_num_pkt_prd = tx_thr_num_pkt_prd; dwc->tx_max_burst_prd = tx_max_burst_prd; - dwc->imod_interval = 0; - dwc->tx_fifo_resize_max_num = tx_fifo_resize_max_num; + + dwc->num_hc_interrupters = num_hc_interrupters; } /* check whether the core supports IMOD */ @@ -1770,21 +1889,19 @@ static void dwc3_check_params(struct dwc3 *dwc) unsigned int hwparam_gen = DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3); - /* Check for proper value of imod_interval */ - if (dwc->imod_interval && !dwc3_has_imod(dwc)) { - dev_warn(dwc->dev, "Interrupt moderation not supported\n"); - dwc->imod_interval = 0; - } - /* + * Enable IMOD for all supporting controllers. + * + * Particularly, DWC_usb3 v3.00a must enable this feature for + * the following reason: + * * Workaround for STAR 9000961433 which affects only version * 3.00a of the DWC_usb3 core. This prevents the controller * interrupt from being masked while handling events. IMOD * allows us to work around this issue. Enable it for the * affected version. */ - if (!dwc->imod_interval && - DWC3_VER_IS(DWC3, 300A)) + if (dwc3_has_imod((dwc))) dwc->imod_interval = 1; /* Check the maximum_speed parameter */ @@ -1872,7 +1989,7 @@ static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc) struct extcon_dev *edev = NULL; const char *name; - if (device_property_read_bool(dev, "extcon")) + if (device_property_present(dev, "extcon")) return extcon_get_edev_by_phandle(dev, 0); /* @@ -2036,26 +2153,32 @@ static int dwc3_get_num_ports(struct dwc3 *dwc) return 0; } -static int dwc3_probe(struct platform_device *pdev) +static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc) { - struct device *dev = &pdev->dev; - struct resource *res, dwc_res; - unsigned int hw_mode; - void __iomem *regs; - struct dwc3 *dwc; - int ret; + struct power_supply *usb_psy; + const char *usb_psy_name; + int ret; - dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL); - if (!dwc) - return -ENOMEM; + ret = device_property_read_string(dwc->dev, "usb-psy-name", &usb_psy_name); + if (ret < 0) + return NULL; - dwc->dev = dev; + usb_psy = power_supply_get_by_name(usb_psy_name); + if (!usb_psy) + return ERR_PTR(-EPROBE_DEFER); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "missing memory resource\n"); - return -ENODEV; - } + return usb_psy; +} + +int dwc3_core_probe(const struct dwc3_probe_data *data) +{ + struct dwc3 *dwc = data->dwc; + struct device *dev = dwc->dev; + struct resource dwc_res; + unsigned int hw_mode; + void __iomem *regs; + struct resource *res = data->res; + int ret; dwc->xhci_resources[0].start = res->start; dwc->xhci_resources[0].end = dwc->xhci_resources[0].start + @@ -2090,15 +2213,23 @@ static int dwc3_probe(struct platform_device *pdev) dwc3_get_properties(dwc); - dwc->reset = devm_reset_control_array_get_optional_shared(dev); - if (IS_ERR(dwc->reset)) { - ret = PTR_ERR(dwc->reset); - goto err_put_psy; - } + dwc3_get_software_properties(dwc, &data->properties); - ret = dwc3_get_clocks(dwc); - if (ret) - goto err_put_psy; + dwc->usb_psy = dwc3_get_usb_power_supply(dwc); + if (IS_ERR(dwc->usb_psy)) + return dev_err_probe(dev, PTR_ERR(dwc->usb_psy), "couldn't get usb power supply\n"); + + if (!data->ignore_clocks_and_resets) { + dwc->reset = devm_reset_control_array_get_optional_shared(dev); + if (IS_ERR(dwc->reset)) { + ret = PTR_ERR(dwc->reset); + goto err_put_psy; + } + + ret = dwc3_get_clocks(dwc); + if (ret) + goto err_put_psy; + } ret = reset_control_deassert(dwc->reset); if (ret) @@ -2114,10 +2245,10 @@ static int dwc3_probe(struct platform_device *pdev) goto err_disable_clks; } - platform_set_drvdata(pdev, dwc); + dev_set_drvdata(dev, dwc); dwc3_cache_hwparams(dwc); - if (!dwc->sysdev_is_parent && + if (!dev_is_pci(dwc->sysdev) && DWC3_GHWPARAMS0_AWIDTH(dwc->hwparams.hwparams0) == 64) { ret = dma_set_mask_and_coherent(dwc->sysdev, DMA_BIT_MASK(64)); if (ret) @@ -2175,9 +2306,11 @@ static int dwc3_probe(struct platform_device *pdev) dwc3_check_params(dwc); dwc3_debugfs_init(dwc); - ret = dwc3_core_init_mode(dwc); - if (ret) - goto err_exit_debugfs; + if (!data->skip_core_init_mode) { + ret = dwc3_core_init_mode(dwc); + if (ret) + goto err_exit_debugfs; + } pm_runtime_put(dev); @@ -2209,12 +2342,37 @@ err_put_psy: return ret; } +EXPORT_SYMBOL_GPL(dwc3_core_probe); -static void dwc3_remove(struct platform_device *pdev) +static int dwc3_probe(struct platform_device *pdev) { - struct dwc3 *dwc = platform_get_drvdata(pdev); + struct dwc3_probe_data probe_data = {}; + struct resource *res; + struct dwc3 *dwc; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "missing memory resource\n"); + return -ENODEV; + } + + dwc = devm_kzalloc(&pdev->dev, sizeof(*dwc), GFP_KERNEL); + if (!dwc) + return -ENOMEM; - pm_runtime_get_sync(&pdev->dev); + dwc->dev = &pdev->dev; + dwc->glue_ops = NULL; + + probe_data.dwc = dwc; + probe_data.res = res; + probe_data.properties = DWC3_DEFAULT_PROPERTIES; + + return dwc3_core_probe(&probe_data); +} + +void dwc3_core_remove(struct dwc3 *dwc) +{ + pm_runtime_get_sync(dwc->dev); dwc3_core_exit_mode(dwc); dwc3_debugfs_exit(dwc); @@ -2222,22 +2380,28 @@ static void dwc3_remove(struct platform_device *pdev) dwc3_core_exit(dwc); dwc3_ulpi_exit(dwc); - pm_runtime_allow(&pdev->dev); - pm_runtime_disable(&pdev->dev); - pm_runtime_dont_use_autosuspend(&pdev->dev); - pm_runtime_put_noidle(&pdev->dev); + pm_runtime_allow(dwc->dev); + pm_runtime_disable(dwc->dev); + pm_runtime_dont_use_autosuspend(dwc->dev); + pm_runtime_put_noidle(dwc->dev); /* * HACK: Clear the driver data, which is currently accessed by parent * glue drivers, before allowing the parent to suspend. */ - platform_set_drvdata(pdev, NULL); - pm_runtime_set_suspended(&pdev->dev); + dev_set_drvdata(dwc->dev, NULL); + pm_runtime_set_suspended(dwc->dev); dwc3_free_event_buffers(dwc); if (dwc->usb_psy) power_supply_put(dwc->usb_psy); } +EXPORT_SYMBOL_GPL(dwc3_core_remove); + +static void dwc3_remove(struct platform_device *pdev) +{ + dwc3_core_remove(platform_get_drvdata(pdev)); +} #ifdef CONFIG_PM static int dwc3_core_init_for_resume(struct dwc3 *dwc) @@ -2270,12 +2434,28 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) { u32 reg; int i; + int ret; + + if (!pm_runtime_suspended(dwc->dev) && !PMSG_IS_AUTO(msg)) { + dwc->susphy_state = (dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)) & + DWC3_GUSB2PHYCFG_SUSPHY) || + (dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)) & + DWC3_GUSB3PIPECTL_SUSPHY); + /* + * TI AM62 platform requires SUSPHY to be + * enabled for system suspend to work. + */ + if (!dwc->susphy_state) + dwc3_enable_susphy(dwc, true); + } switch (dwc->current_dr_role) { case DWC3_GCTL_PRTCAP_DEVICE: if (pm_runtime_suspended(dwc->dev)) break; - dwc3_gadget_suspend(dwc); + ret = dwc3_gadget_suspend(dwc); + if (ret) + return ret; synchronize_irq(dwc->irq_gadget); dwc3_core_exit(dwc); break; @@ -2310,7 +2490,9 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) break; if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) { - dwc3_gadget_suspend(dwc); + ret = dwc3_gadget_suspend(dwc); + if (ret) + return ret; synchronize_irq(dwc->irq_gadget); } @@ -2337,7 +2519,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) if (ret) return ret; - dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE); + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, true); dwc3_gadget_resume(dwc); break; case DWC3_GCTL_PRTCAP_HOST: @@ -2345,7 +2527,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) ret = dwc3_core_init_for_resume(dwc); if (ret) return ret; - dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST); + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST, true); break; } /* Restore GUSB2PHYCFG bits that were modified in suspend */ @@ -2374,7 +2556,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) if (ret) return ret; - dwc3_set_prtcap(dwc, dwc->current_dr_role); + dwc3_set_prtcap(dwc, dwc->current_dr_role, true); dwc3_otg_init(dwc); if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST) { @@ -2389,6 +2571,11 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) break; } + if (!PMSG_IS_AUTO(msg)) { + /* restore SUSPHY state to that before system suspend. */ + dwc3_enable_susphy(dwc, dwc->susphy_state); + } + return 0; } @@ -2408,9 +2595,8 @@ static int dwc3_runtime_checks(struct dwc3 *dwc) return 0; } -static int dwc3_runtime_suspend(struct device *dev) +int dwc3_runtime_suspend(struct dwc3 *dwc) { - struct dwc3 *dwc = dev_get_drvdata(dev); int ret; if (dwc3_runtime_checks(dwc)) @@ -2422,10 +2608,11 @@ static int dwc3_runtime_suspend(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(dwc3_runtime_suspend); -static int dwc3_runtime_resume(struct device *dev) +int dwc3_runtime_resume(struct dwc3 *dwc) { - struct dwc3 *dwc = dev_get_drvdata(dev); + struct device *dev = dwc->dev; int ret; ret = dwc3_resume_common(dwc, PMSG_AUTO_RESUME); @@ -2434,7 +2621,11 @@ static int dwc3_runtime_resume(struct device *dev) switch (dwc->current_dr_role) { case DWC3_GCTL_PRTCAP_DEVICE: - dwc3_gadget_process_pending_events(dwc); + if (dwc->pending_events) { + pm_runtime_put(dev); + dwc->pending_events = false; + enable_irq(dwc->irq_gadget); + } break; case DWC3_GCTL_PRTCAP_HOST: default: @@ -2446,10 +2637,11 @@ static int dwc3_runtime_resume(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(dwc3_runtime_resume); -static int dwc3_runtime_idle(struct device *dev) +int dwc3_runtime_idle(struct dwc3 *dwc) { - struct dwc3 *dwc = dev_get_drvdata(dev); + struct device *dev = dwc->dev; switch (dwc->current_dr_role) { case DWC3_GCTL_PRTCAP_DEVICE: @@ -2462,17 +2654,32 @@ static int dwc3_runtime_idle(struct device *dev) break; } - pm_runtime_mark_last_busy(dev); pm_runtime_autosuspend(dev); return 0; } +EXPORT_SYMBOL_GPL(dwc3_runtime_idle); + +static int dwc3_plat_runtime_suspend(struct device *dev) +{ + return dwc3_runtime_suspend(dev_get_drvdata(dev)); +} + +static int dwc3_plat_runtime_resume(struct device *dev) +{ + return dwc3_runtime_resume(dev_get_drvdata(dev)); +} + +static int dwc3_plat_runtime_idle(struct device *dev) +{ + return dwc3_runtime_idle(dev_get_drvdata(dev)); +} #endif /* CONFIG_PM */ #ifdef CONFIG_PM_SLEEP -static int dwc3_suspend(struct device *dev) +int dwc3_pm_suspend(struct dwc3 *dwc) { - struct dwc3 *dwc = dev_get_drvdata(dev); + struct device *dev = dwc->dev; int ret; ret = dwc3_suspend_common(dwc, PMSG_SUSPEND); @@ -2483,31 +2690,33 @@ static int dwc3_suspend(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(dwc3_pm_suspend); -static int dwc3_resume(struct device *dev) +int dwc3_pm_resume(struct dwc3 *dwc) { - struct dwc3 *dwc = dev_get_drvdata(dev); - int ret; + struct device *dev = dwc->dev; + int ret = 0; pinctrl_pm_select_default_state(dev); pm_runtime_disable(dev); - pm_runtime_set_active(dev); + ret = pm_runtime_set_active(dev); + if (ret) + goto out; ret = dwc3_resume_common(dwc, PMSG_RESUME); - if (ret) { + if (ret) pm_runtime_set_suspended(dev); - return ret; - } +out: pm_runtime_enable(dev); - return 0; + return ret; } +EXPORT_SYMBOL_GPL(dwc3_pm_resume); -static void dwc3_complete(struct device *dev) +void dwc3_pm_complete(struct dwc3 *dwc) { - struct dwc3 *dwc = dev_get_drvdata(dev); u32 reg; if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST && @@ -2517,15 +2726,60 @@ static void dwc3_complete(struct device *dev) dwc3_writel(dwc->regs, DWC3_GUCTL3, reg); } } +EXPORT_SYMBOL_GPL(dwc3_pm_complete); + +int dwc3_pm_prepare(struct dwc3 *dwc) +{ + struct device *dev = dwc->dev; + + /* + * Indicate to the PM core that it may safely leave the device in + * runtime suspend if runtime-suspended already in device mode. + */ + if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE && + pm_runtime_suspended(dev) && + !dev_pinctrl(dev)) + return 1; + + return 0; +} +EXPORT_SYMBOL_GPL(dwc3_pm_prepare); + +static int dwc3_plat_suspend(struct device *dev) +{ + return dwc3_pm_suspend(dev_get_drvdata(dev)); +} + +static int dwc3_plat_resume(struct device *dev) +{ + return dwc3_pm_resume(dev_get_drvdata(dev)); +} + +static void dwc3_plat_complete(struct device *dev) +{ + dwc3_pm_complete(dev_get_drvdata(dev)); +} + +static int dwc3_plat_prepare(struct device *dev) +{ + return dwc3_pm_prepare(dev_get_drvdata(dev)); +} #else -#define dwc3_complete NULL +#define dwc3_plat_complete NULL +#define dwc3_plat_prepare NULL #endif /* CONFIG_PM_SLEEP */ static const struct dev_pm_ops dwc3_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume) - .complete = dwc3_complete, - SET_RUNTIME_PM_OPS(dwc3_runtime_suspend, dwc3_runtime_resume, - dwc3_runtime_idle) + SET_SYSTEM_SLEEP_PM_OPS(dwc3_plat_suspend, dwc3_plat_resume) + .complete = dwc3_plat_complete, + .prepare = dwc3_plat_prepare, + /* + * Runtime suspend halts the controller on disconnection. It relies on + * platforms with custom connection notification to start the controller + * again. + */ + SET_RUNTIME_PM_OPS(dwc3_plat_runtime_suspend, dwc3_plat_runtime_resume, + dwc3_plat_runtime_idle) }; #ifdef CONFIG_OF @@ -2554,7 +2808,7 @@ MODULE_DEVICE_TABLE(acpi, dwc3_acpi_match); static struct platform_driver dwc3_driver = { .probe = dwc3_probe, - .remove_new = dwc3_remove, + .remove = dwc3_remove, .driver = { .name = "dwc3", .of_match_table = of_match_ptr(of_dwc3_match), |
