diff options
32 files changed, 469 insertions, 92 deletions
diff --git a/Documentation/devicetree/bindings/usb/dwc2.txt b/Documentation/devicetree/bindings/usb/dwc2.txt index 49eac0dc86b0..aafff3a6904d 100644 --- a/Documentation/devicetree/bindings/usb/dwc2.txt +++ b/Documentation/devicetree/bindings/usb/dwc2.txt @@ -42,6 +42,8 @@ Refer to phy/phy-bindings.txt for generic phy consumer properties - g-rx-fifo-size: size of rx fifo size in gadget mode. - g-np-tx-fifo-size: size of non-periodic tx fifo size in gadget mode. - g-tx-fifo-size: size of periodic tx fifo per endpoint (except ep0) in gadget mode. +- snps,need-phy-for-wake: If present indicates that the phy needs to be left + on for remote wakeup during suspend. - snps,reset-phy-on-wake: If present indicates that we need to reset the PHY when we detect a wakeup. This is due to a hardware errata. @@ -58,4 +60,5 @@ Example: clock-names = "otg"; phys = <&usbphy>; phy-names = "usb2-phy"; + snps,need-phy-for-wake; }; diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt index 8e5265e9f658..66780a47ad85 100644 --- a/Documentation/devicetree/bindings/usb/dwc3.txt +++ b/Documentation/devicetree/bindings/usb/dwc3.txt @@ -64,6 +64,8 @@ Optional properties: - snps,dis_u2_susphy_quirk: when set core will disable USB2 suspend phy. - snps,dis_enblslpm_quirk: when set clears the enblslpm in GUSB2PHYCFG, disabling the suspend signal to the PHY. + - snps,dis-u1-entry-quirk: set if link entering into U1 needs to be disabled. + - snps,dis-u2-entry-quirk: set if link entering into U2 needs to be disabled. - snps,dis_rxdet_inp3_quirk: when set core will disable receiver detection in PHY P3 power state. - snps,dis-u2-freeclk-exists-quirk: when set, clear the u2_freeclk_exists diff --git a/Documentation/devicetree/bindings/usb/renesas_usb3.txt b/Documentation/devicetree/bindings/usb/renesas,usb3.txt index 35039e720515..35039e720515 100644 --- a/Documentation/devicetree/bindings/usb/renesas_usb3.txt +++ b/Documentation/devicetree/bindings/usb/renesas,usb3.txt diff --git a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt b/Documentation/devicetree/bindings/usb/renesas,usbhs.txt index e39255ea6e4f..e39255ea6e4f 100644 --- a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt +++ b/Documentation/devicetree/bindings/usb/renesas,usbhs.txt diff --git a/arch/arm/boot/dts/rk3288-veyron.dtsi b/arch/arm/boot/dts/rk3288-veyron.dtsi index 1252522392c7..1d8bfed7830c 100644 --- a/arch/arm/boot/dts/rk3288-veyron.dtsi +++ b/arch/arm/boot/dts/rk3288-veyron.dtsi @@ -424,6 +424,7 @@ &usb_host1 { status = "okay"; + snps,need-phy-for-wake; }; &usb_otg { @@ -432,6 +433,7 @@ assigned-clocks = <&cru SCLK_USBPHY480M_SRC>; assigned-clock-parents = <&usbphy0>; dr_mode = "host"; + snps,need-phy-for-wake; }; &vopb { diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c index 6b8ef01472e9..d5cf953b4337 100644 --- a/drivers/soc/qcom/qcom-geni-se.c +++ b/drivers/soc/qcom/qcom-geni-se.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. +#include <linux/acpi.h> #include <linux/clk.h> #include <linux/slab.h> #include <linux/dma-mapping.h> @@ -450,6 +451,9 @@ int geni_se_resources_off(struct geni_se *se) { int ret; + if (has_acpi_companion(se->dev)) + return 0; + ret = pinctrl_pm_select_sleep_state(se->dev); if (ret) return ret; @@ -487,6 +491,9 @@ int geni_se_resources_on(struct geni_se *se) { int ret; + if (has_acpi_companion(se->dev)) + return 0; + ret = geni_se_clks_on(se); if (ret) return ret; @@ -724,12 +731,14 @@ static int geni_se_probe(struct platform_device *pdev) if (IS_ERR(wrapper->base)) return PTR_ERR(wrapper->base); - wrapper->ahb_clks[0].id = "m-ahb"; - wrapper->ahb_clks[1].id = "s-ahb"; - ret = devm_clk_bulk_get(dev, NUM_AHB_CLKS, wrapper->ahb_clks); - if (ret) { - dev_err(dev, "Err getting AHB clks %d\n", ret); - return ret; + if (!has_acpi_companion(&pdev->dev)) { + wrapper->ahb_clks[0].id = "m-ahb"; + wrapper->ahb_clks[1].id = "s-ahb"; + ret = devm_clk_bulk_get(dev, NUM_AHB_CLKS, wrapper->ahb_clks); + if (ret) { + dev_err(dev, "Err getting AHB clks %d\n", ret); + return ret; + } } dev_set_drvdata(dev, wrapper); diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 152ac41dfb2d..d08d070a0fb6 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -861,6 +861,9 @@ struct dwc2_hregs_backup { * @hibernated: True if core is hibernated * @reset_phy_on_wake: Quirk saying that we should assert PHY reset on a * remote wakeup. + * @phy_off_for_suspend: Status of whether we turned the PHY off at suspend. + * @need_phy_for_wake: Quirk saying that we should keep the PHY on at + * suspend if we need USB to wake us up. * @frame_number: Frame number read from the core. For both device * and host modes. The value ranges are from 0 * to HFNUM_MAX_FRNUM. @@ -1049,6 +1052,8 @@ struct dwc2_hsotg { unsigned int ll_hw_enabled:1; unsigned int hibernated:1; unsigned int reset_phy_on_wake:1; + unsigned int need_phy_for_wake:1; + unsigned int phy_off_for_suspend:1; u16 frame_number; struct phy *phy; @@ -1438,6 +1443,7 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg); int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg); int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, int reset); +bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2); static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) { schedule_work(&hsotg->phy_reset_work); } #else @@ -1463,6 +1469,8 @@ static inline int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg) static inline int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, int reset) { return 0; } +static inline bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2) +{ return false; } static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) {} #endif diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 2192a2873c7c..ee144ff8af5b 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4685,7 +4685,6 @@ fail2: spin_unlock_irqrestore(&hsotg->lock, flags); urb->hcpriv = NULL; kfree(qtd); - qtd = NULL; fail1: if (qh_allocated) { struct dwc2_qtd *qtd2, *qtd2_tmp; @@ -5587,3 +5586,22 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, dev_dbg(hsotg->dev, "Host hibernation restore complete\n"); return ret; } + +bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2) +{ + struct usb_device *root_hub = dwc2_hsotg_to_hcd(dwc2)->self.root_hub; + + /* If the controller isn't allowed to wakeup then we can power off. */ + if (!device_may_wakeup(dwc2->dev)) + return true; + + /* + * We don't want to power off the PHY if something under the + * root hub has wakeup enabled. + */ + if (usb_wakeup_enabled_descendants(root_hub)) + return false; + + /* No reason to keep the PHY powered, so allow poweroff */ + return true; +} diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index ce6445a06588..8ca6d12a6f57 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -582,7 +582,6 @@ static inline void dwc2_hcd_qtd_unlink_and_free(struct dwc2_hsotg *hsotg, { list_del(&qtd->qtd_list_entry); kfree(qtd); - qtd = NULL; } /* Descriptor DMA support functions */ diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index 5949262ff669..55f841a54015 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -76,6 +76,7 @@ static void dwc2_set_s3c6400_params(struct dwc2_hsotg *hsotg) struct dwc2_core_params *p = &hsotg->params; p->power_down = 0; + p->phy_utmi_width = 8; } static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg) diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index e98d7812da2d..80fd3c6dcd1c 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -438,6 +438,10 @@ static int dwc2_driver_probe(struct platform_device *dev) if (retval) goto error; + hsotg->need_phy_for_wake = + of_property_read_bool(dev->dev.of_node, + "snps,need-phy-for-wake"); + /* * Reset before dwc2_get_hwparams() then it could get power-on real * reset value form registers. @@ -469,6 +473,14 @@ static int dwc2_driver_probe(struct platform_device *dev) hsotg->gadget_enabled = 1; } + /* + * If we need PHY for wakeup we must be wakeup capable. + * When we have a device that can wake without the PHY we + * can adjust this condition. + */ + if (hsotg->need_phy_for_wake) + device_set_wakeup_capable(&dev->dev, true); + hsotg->reset_phy_on_wake = of_property_read_bool(dev->dev.of_node, "snps,reset-phy-on-wake"); @@ -507,13 +519,17 @@ error: static int __maybe_unused dwc2_suspend(struct device *dev) { struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); + bool is_device_mode = dwc2_is_device_mode(dwc2); int ret = 0; - if (dwc2_is_device_mode(dwc2)) + if (is_device_mode) dwc2_hsotg_suspend(dwc2); - if (dwc2->ll_hw_enabled) + if (dwc2->ll_hw_enabled && + (is_device_mode || dwc2_host_can_poweroff_phy(dwc2))) { ret = __dwc2_lowlevel_hw_disable(dwc2); + dwc2->phy_off_for_suspend = true; + } return ret; } @@ -523,11 +539,12 @@ static int __maybe_unused dwc2_resume(struct device *dev) struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); int ret = 0; - if (dwc2->ll_hw_enabled) { + if (dwc2->phy_off_for_suspend && dwc2->ll_hw_enabled) { ret = __dwc2_lowlevel_hw_enable(dwc2); if (ret) return ret; } + dwc2->phy_off_for_suspend = false; if (dwc2_is_device_mode(dwc2)) ret = dwc2_hsotg_resume(dwc2); diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 4a62045cc812..89abc6078703 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -128,7 +128,7 @@ config USB_DWC3_QCOM tristate "Qualcomm Platform" depends on ARCH_QCOM || COMPILE_TEST depends on EXTCON || !EXTCON - depends on OF + depends on (OF || ACPI) default USB_DWC3 help Some Qualcomm SoCs use DesignWare Core IP for USB2/3 diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 4aff1d8dbc4f..c9bb93a2c81e 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1282,6 +1282,10 @@ static void dwc3_get_properties(struct dwc3 *dwc) "snps,dis_u2_susphy_quirk"); dwc->dis_enblslpm_quirk = device_property_read_bool(dev, "snps,dis_enblslpm_quirk"); + dwc->dis_u1_entry_quirk = device_property_read_bool(dev, + "snps,dis-u1-entry-quirk"); + dwc->dis_u2_entry_quirk = device_property_read_bool(dev, + "snps,dis-u2-entry-quirk"); dwc->dis_rxdet_inp3_quirk = device_property_read_bool(dev, "snps,dis_rxdet_inp3_quirk"); dwc->dis_u2_freeclk_exists_quirk = device_property_read_bool(dev, @@ -1423,11 +1427,6 @@ static int dwc3_probe(struct platform_device *pdev) dwc->regs = regs; dwc->regs_size = resource_size(&dwc_res); - if (!dwc3_core_is_valid(dwc)) { - dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n"); - return -ENODEV; - } - dwc3_get_properties(dwc); dwc->reset = devm_reset_control_get_optional_shared(dev, NULL); @@ -1460,6 +1459,12 @@ static int dwc3_probe(struct platform_device *pdev) if (ret) goto unprepare_clks; + if (!dwc3_core_is_valid(dwc)) { + dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n"); + ret = -ENODEV; + goto disable_clks; + } + platform_set_drvdata(pdev, dwc); dwc3_cache_hwparams(dwc); @@ -1525,6 +1530,7 @@ err1: pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); +disable_clks: clk_bulk_disable(dwc->num_clks, dwc->clks); unprepare_clks: clk_bulk_unprepare(dwc->num_clks, dwc->clks); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index f19cbeb01087..3dd783b889cb 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -649,7 +649,6 @@ struct dwc3_event_buffer { * @cancelled_list: list of cancelled requests for this endpoint * @pending_list: list of pending requests for this endpoint * @started_list: list of started requests on this endpoint - * @lock: spinlock for endpoint request queue traversal * @regs: pointer to first endpoint register * @trb_pool: array of transaction buffers * @trb_pool_dma: dma address of @trb_pool @@ -677,7 +676,6 @@ struct dwc3_ep { struct list_head pending_list; struct list_head started_list; - spinlock_t lock; void __iomem *regs; struct dwc3_trb *trb_pool; @@ -1014,6 +1012,8 @@ struct dwc3_scratchpad_array { * @dis_u2_susphy_quirk: set if we disable usb2 suspend phy * @dis_enblslpm_quirk: set if we clear enblslpm in GUSB2PHYCFG, * disabling the suspend signal to the PHY. + * @dis_u1_entry_quirk: set if link entering into U1 state needs to be disabled. + * @dis_u2_entry_quirk: set if link entering into U2 state needs to be disabled. * @dis_rxdet_inp3_quirk: set if we disable Rx.Detect in P3 * @dis_u2_freeclk_exists_quirk : set if we clear u2_freeclk_exists * in GUSB2PHYCFG, specify that USB2 PHY doesn't @@ -1205,6 +1205,8 @@ struct dwc3 { unsigned dis_u3_susphy_quirk:1; unsigned dis_u2_susphy_quirk:1; unsigned dis_enblslpm_quirk:1; + unsigned dis_u1_entry_quirk:1; + unsigned dis_u2_entry_quirk:1; unsigned dis_rxdet_inp3_quirk:1; unsigned dis_u2_freeclk_exists_quirk:1; unsigned dis_del_phy_power_chg_quirk:1; diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index 2aec31a2eacb..bca7e92a10e9 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -11,9 +11,7 @@ * - Control registers for each USB2 Ports * - Control registers for the USB PHY layer * - SuperSpeed PHY can be enabled only if port is used - * - * TOFIX: - * - Add dynamic OTG switching with ID change interrupt + * - Dynamic OTG switching with ID change interrupt */ #include <linux/module.h> @@ -348,6 +346,22 @@ static enum usb_role dwc3_meson_g12a_role_get(struct device *dev) USB_ROLE_HOST : USB_ROLE_DEVICE; } +static irqreturn_t dwc3_meson_g12a_irq_thread(int irq, void *data) +{ + struct dwc3_meson_g12a *priv = data; + enum phy_mode otg_id; + + otg_id = dwc3_meson_g12a_get_id(priv); + if (otg_id != priv->otg_phy_mode) { + if (dwc3_meson_g12a_otg_mode_set(priv, otg_id)) + dev_warn(priv->dev, "Failed to switch OTG mode\n"); + } + + regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0); + + return IRQ_HANDLED; +} + static struct device *dwc3_meson_g12_find_child(struct device *dev, const char *compatible) { @@ -374,7 +388,7 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) void __iomem *base; struct resource *res; enum phy_mode otg_id; - int ret, i; + int ret, i, irq; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -436,6 +450,19 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) /* Get dr_mode */ priv->otg_mode = usb_get_dr_mode(dev); + if (priv->otg_mode == USB_DR_MODE_OTG) { + /* Ack irq before registering */ + regmap_update_bits(priv->regmap, USB_R5, + USB_R5_ID_DIG_IRQ, 0); + + irq = platform_get_irq(pdev, 0); + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + dwc3_meson_g12a_irq_thread, + IRQF_ONESHOT, pdev->name, priv); + if (ret) + return ret; + } + dwc3_meson_g12a_usb_init(priv); /* Init PHYs */ @@ -460,7 +487,6 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) /* Setup OTG mode corresponding to the ID pin */ if (priv->otg_mode == USB_DR_MODE_OTG) { - /* TOFIX Handle ID mode toggling via IRQ */ otg_id = dwc3_meson_g12a_get_id(priv); if (otg_id != priv->otg_phy_mode) { if (dwc3_meson_g12a_otg_mode_set(priv, otg_id)) diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 8cced3609e24..f9b550081550 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -34,6 +34,7 @@ #define PCI_DEVICE_ID_INTEL_CNPLP 0x9dee #define PCI_DEVICE_ID_INTEL_CNPH 0xa36e #define PCI_DEVICE_ID_INTEL_ICLLP 0x34ee +#define PCI_DEVICE_ID_INTEL_EHLLP 0x4b7e #define PCI_INTEL_BXT_DSM_GUID "732b85d5-b7a7-4a1b-9ba0-4bbd00ffd511" #define PCI_INTEL_BXT_FUNC_PMU_PWR 4 @@ -339,6 +340,9 @@ static const struct pci_device_id dwc3_pci_id_table[] = { { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICLLP), (kernel_ulong_t) &dwc3_pci_intel_properties, }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_EHLLP), + (kernel_ulong_t) &dwc3_pci_intel_properties, }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_NL_USB), (kernel_ulong_t) &dwc3_pci_amd_properties, }, { } /* Terminating Entry */ diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index 184df4daa590..261af9e38ddd 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -4,6 +4,7 @@ * Inspired by dwc3-of-simple.c */ +#include <linux/acpi.h> #include <linux/io.h> #include <linux/of.h> #include <linux/clk.h> @@ -38,6 +39,20 @@ #define PWR_EVNT_LPM_IN_L2_MASK BIT(4) #define PWR_EVNT_LPM_OUT_L2_MASK BIT(5) +#define SDM845_QSCRATCH_BASE_OFFSET 0xf8800 +#define SDM845_QSCRATCH_SIZE 0x400 +#define SDM845_DWC3_CORE_SIZE 0xcd00 + +struct dwc3_acpi_pdata { + u32 qscratch_base_offset; + u32 qscratch_base_size; + u32 dwc3_core_base_size; + int hs_phy_irq_index; + int dp_hs_phy_irq_index; + int dm_hs_phy_irq_index; + int ss_phy_irq_index; +}; + struct dwc3_qcom { struct device *dev; void __iomem *qscratch_base; @@ -56,6 +71,8 @@ struct dwc3_qcom { struct notifier_block vbus_nb; struct notifier_block host_nb; + const struct dwc3_acpi_pdata *acpi_pdata; + enum usb_dr_mode mode; bool is_suspended; bool pm_suspended; @@ -300,12 +317,27 @@ static void dwc3_qcom_select_utmi_clk(struct dwc3_qcom *qcom) PIPE_UTMI_CLK_DIS); } +static int dwc3_qcom_get_irq(struct platform_device *pdev, + const char *name, int num) +{ + struct device_node *np = pdev->dev.of_node; + int ret; + + if (np) + ret = platform_get_irq_byname(pdev, name); + else + ret = platform_get_irq(pdev, num); + + return ret; +} + static int dwc3_qcom_setup_irq(struct platform_device *pdev) { struct dwc3_qcom *qcom = platform_get_drvdata(pdev); + const struct dwc3_acpi_pdata *pdata = qcom->acpi_pdata; int irq, ret; - - irq = platform_get_irq_byname(pdev, "hs_phy_irq"); + irq = dwc3_qcom_get_irq(pdev, "hs_phy_irq", + pdata ? pdata->hs_phy_irq_index : -1); if (irq > 0) { /* Keep wakeup interrupts disabled until suspend */ irq_set_status_flags(irq, IRQ_NOAUTOEN); @@ -320,7 +352,8 @@ static int dwc3_qcom_setup_irq(struct platform_device *pdev) qcom->hs_phy_irq = irq; } - irq = platform_get_irq_byname(pdev, "dp_hs_phy_irq"); + irq = dwc3_qcom_get_irq(pdev, "dp_hs_phy_irq", + pdata ? pdata->dp_hs_phy_irq_index : -1); if (irq > 0) { irq_set_status_flags(irq, IRQ_NOAUTOEN); ret = devm_request_threaded_irq(qcom->dev, irq, NULL, @@ -334,7 +367,8 @@ static int dwc3_qcom_setup_irq(struct platform_device *pdev) qcom->dp_hs_phy_irq = irq; } - irq = platform_get_irq_byname(pdev, "dm_hs_phy_irq"); + irq = dwc3_qcom_get_irq(pdev, "dm_hs_phy_irq", + pdata ? pdata->dm_hs_phy_irq_index : -1); if (irq > 0) { irq_set_status_flags(irq, IRQ_NOAUTOEN); ret = devm_request_threaded_irq(qcom->dev, irq, NULL, @@ -348,7 +382,8 @@ static int dwc3_qcom_setup_irq(struct platform_device *pdev) qcom->dm_hs_phy_irq = irq; } - irq = platform_get_irq_byname(pdev, "ss_phy_irq"); + irq = dwc3_qcom_get_irq(pdev, "ss_phy_irq", + pdata ? pdata->ss_phy_irq_index : -1); if (irq > 0) { irq_set_status_flags(irq, IRQ_NOAUTOEN); ret = devm_request_threaded_irq(qcom->dev, irq, NULL, @@ -371,11 +406,14 @@ static int dwc3_qcom_clk_init(struct dwc3_qcom *qcom, int count) struct device_node *np = dev->of_node; int i; - qcom->num_clocks = count; - - if (!count) + if (!np || !count) return 0; + if (count < 0) + return count; + + qcom->num_clocks = count; + qcom->clks = devm_kcalloc(dev, qcom->num_clocks, sizeof(struct clk *), GFP_KERNEL); if (!qcom->clks) @@ -409,12 +447,115 @@ static int dwc3_qcom_clk_init(struct dwc3_qcom *qcom, int count) return 0; } -static int dwc3_qcom_probe(struct platform_device *pdev) +static const struct property_entry dwc3_qcom_acpi_properties[] = { + PROPERTY_ENTRY_STRING("dr_mode", "host"), + {} +}; + +static int dwc3_qcom_acpi_register_core(struct platform_device *pdev) +{ + struct dwc3_qcom *qcom = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + struct resource *res, *child_res = NULL; + int irq; + int ret; + + qcom->dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO); + if (!qcom->dwc3) + return -ENOMEM; + + qcom->dwc3->dev.parent = dev; + qcom->dwc3->dev.type = dev->type; + qcom->dwc3->dev.dma_mask = dev->dma_mask; + qcom->dwc3->dev.dma_parms = dev->dma_parms; + qcom->dwc3->dev.coherent_dma_mask = dev->coherent_dma_mask; + + child_res = kcalloc(2, sizeof(*child_res), GFP_KERNEL); + if (!child_res) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get memory resource\n"); + ret = -ENODEV; + goto out; + } + + child_res[0].flags = res->flags; + child_res[0].start = res->start; + child_res[0].end = child_res[0].start + + qcom->acpi_pdata->dwc3_core_base_size; + + irq = platform_get_irq(pdev, 0); + child_res[1].flags = IORESOURCE_IRQ; + child_res[1].start = child_res[1].end = irq; + + ret = platform_device_add_resources(qcom->dwc3, child_res, 2); + if (ret) { + dev_err(&pdev->dev, "failed to add resources\n"); + goto out; + } + + ret = platform_device_add_properties(qcom->dwc3, + dwc3_qcom_acpi_properties); + if (ret < 0) { + dev_err(&pdev->dev, "failed to add properties\n"); + goto out; + } + + ret = platform_device_add(qcom->dwc3); + if (ret) + dev_err(&pdev->dev, "failed to add device\n"); + +out: + kfree(child_res); + return ret; +} + +static int dwc3_qcom_of_register_core(struct platform_device *pdev) { + struct dwc3_qcom *qcom = platform_get_drvdata(pdev); struct device_node *np = pdev->dev.of_node, *dwc3_np; struct device *dev = &pdev->dev; + int ret; + + dwc3_np = of_get_child_by_name(np, "dwc3"); + if (!dwc3_np) { + dev_err(dev, "failed to find dwc3 core child\n"); + return -ENODEV; + } + + ret = of_platform_populate(np, NULL, NULL, dev); + if (ret) { + dev_err(dev, "failed to register dwc3 core - %d\n", ret); + return ret; + } + + qcom->dwc3 = of_find_device_by_node(dwc3_np); + if (!qcom->dwc3) { + dev_err(dev, "failed to get dwc3 platform device\n"); + return -ENODEV; + } + + return 0; +} + +static const struct dwc3_acpi_pdata sdm845_acpi_pdata = { + .qscratch_base_offset = SDM845_QSCRATCH_BASE_OFFSET, + .qscratch_base_size = SDM845_QSCRATCH_SIZE, + .dwc3_core_base_size = SDM845_DWC3_CORE_SIZE, + .hs_phy_irq_index = 1, + .dp_hs_phy_irq_index = 4, + .dm_hs_phy_irq_index = 3, + .ss_phy_irq_index = 2 +}; + +static int dwc3_qcom_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; struct dwc3_qcom *qcom; - struct resource *res; + struct resource *res, *parent_res = NULL; int ret, i; bool ignore_pipe_clk; @@ -425,6 +566,14 @@ static int dwc3_qcom_probe(struct platform_device *pdev) platform_set_drvdata(pdev, qcom); qcom->dev = &pdev->dev; + if (has_acpi_companion(dev)) { + qcom->acpi_pdata = acpi_device_get_match_data(dev); + if (!qcom->acpi_pdata) { + dev_err(&pdev->dev, "no supporting ACPI device data\n"); + return -EINVAL; + } + } + qcom->resets = devm_reset_control_array_get_optional_exclusive(dev); if (IS_ERR(qcom->resets)) { ret = PTR_ERR(qcom->resets); @@ -446,15 +595,28 @@ static int dwc3_qcom_probe(struct platform_device *pdev) goto reset_assert; } - ret = dwc3_qcom_clk_init(qcom, of_count_phandle_with_args(np, - "clocks", "#clock-cells")); + ret = dwc3_qcom_clk_init(qcom, of_clk_get_parent_count(np)); if (ret) { dev_err(dev, "failed to get clocks\n"); goto reset_assert; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - qcom->qscratch_base = devm_ioremap_resource(dev, res); + + if (np) { + parent_res = res; + } else { + parent_res = kmemdup(res, sizeof(struct resource), GFP_KERNEL); + if (!parent_res) + return -ENOMEM; + + parent_res->start = res->start + + qcom->acpi_pdata->qscratch_base_offset; + parent_res->end = parent_res->start + + qcom->acpi_pdata->qscratch_base_size; + } + + qcom->qscratch_base = devm_ioremap_resource(dev, parent_res); if (IS_ERR(qcom->qscratch_base)) { dev_err(dev, "failed to map qscratch, err=%d\n", ret); ret = PTR_ERR(qcom->qscratch_base); @@ -462,13 +624,8 @@ static int dwc3_qcom_probe(struct platform_device *pdev) } ret = dwc3_qcom_setup_irq(pdev); - if (ret) - goto clk_disable; - - dwc3_np = of_get_child_by_name(np, "dwc3"); - if (!dwc3_np) { - dev_err(dev, "failed to find dwc3 core child\n"); - ret = -ENODEV; + if (ret) { + dev_err(dev, "failed to setup IRQs, err=%d\n", ret); goto clk_disable; } @@ -481,16 +638,13 @@ static int dwc3_qcom_probe(struct platform_device *pdev) if (ignore_pipe_clk) dwc3_qcom_select_utmi_clk(qcom); - ret = of_platform_populate(np, NULL, NULL, dev); - if (ret) { - dev_err(dev, "failed to register dwc3 core - %d\n", ret); - goto clk_disable; - } + if (np) + ret = dwc3_qcom_of_register_core(pdev); + else + ret = dwc3_qcom_acpi_register_core(pdev); - qcom->dwc3 = of_find_device_by_node(dwc3_np); - if (!qcom->dwc3) { - dev_err(&pdev->dev, "failed to get dwc3 platform device\n"); - ret = -ENODEV; + if (ret) { + dev_err(dev, "failed to register DWC3 Core, err=%d\n", ret); goto depopulate; } @@ -514,7 +668,10 @@ static int dwc3_qcom_probe(struct platform_device *pdev) return 0; depopulate: - of_platform_depopulate(&pdev->dev); + if (np) + of_platform_depopulate(&pdev->dev); + else + platform_device_put(pdev); clk_disable: for (i = qcom->num_clocks - 1; i >= 0; i--) { clk_disable_unprepare(qcom->clks[i]); @@ -601,6 +758,12 @@ static const struct of_device_id dwc3_qcom_of_match[] = { }; MODULE_DEVICE_TABLE(of, dwc3_qcom_of_match); +static const struct acpi_device_id dwc3_qcom_acpi_match[] = { + { "QCOM2430", (unsigned long)&sdm845_acpi_pdata }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, dwc3_qcom_acpi_match); + static struct platform_driver dwc3_qcom_driver = { .probe = dwc3_qcom_probe, .remove = dwc3_qcom_remove, @@ -608,6 +771,7 @@ static struct platform_driver dwc3_qcom_driver = { .name = "dwc3-qcom", .pm = &dwc3_qcom_dev_pm_ops, .of_match_table = dwc3_qcom_of_match, + .acpi_match_table = ACPI_PTR(dwc3_qcom_acpi_match), }, }; diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 8efde178eef4..3996b9c4ff8d 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -379,6 +379,8 @@ static int dwc3_ep0_handle_u1(struct dwc3 *dwc, enum usb_device_state state, if ((dwc->speed != DWC3_DSTS_SUPERSPEED) && (dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS)) return -EINVAL; + if (set && dwc->dis_u1_entry_quirk) + return -EINVAL; reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (set) @@ -401,6 +403,8 @@ static int dwc3_ep0_handle_u2(struct dwc3 *dwc, enum usb_device_state state, if ((dwc->speed != DWC3_DSTS_SUPERSPEED) && (dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS)) return -EINVAL; + if (set && dwc->dis_u2_entry_quirk) + return -EINVAL; reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (set) @@ -626,7 +630,10 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) * nothing is pending from application. */ reg = dwc3_readl(dwc->regs, DWC3_DCTL); - reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA); + if (!dwc->dis_u1_entry_quirk) + reg |= DWC3_DCTL_ACCEPTU1ENA; + if (!dwc->dis_u2_entry_quirk) + reg |= DWC3_DCTL_ACCEPTU2ENA; dwc3_writel(dwc->regs, DWC3_DCTL, reg); } break; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index d67655384eb2..173f5329d3d9 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2073,6 +2073,25 @@ out: return 0; } +static void dwc3_gadget_config_params(struct usb_gadget *g, + struct usb_dcd_config_params *params) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + + /* U1 Device exit Latency */ + if (dwc->dis_u1_entry_quirk) + params->bU1devExitLat = 0; + else + params->bU1devExitLat = DWC3_DEFAULT_U1_DEV_EXIT_LAT; + + /* U2 Device exit Latency */ + if (dwc->dis_u2_entry_quirk) + params->bU2DevExitLat = 0; + else + params->bU2DevExitLat = + cpu_to_le16(DWC3_DEFAULT_U2_DEV_EXIT_LAT); +} + static void dwc3_gadget_set_speed(struct usb_gadget *g, enum usb_device_speed speed) { @@ -2142,6 +2161,7 @@ static const struct usb_gadget_ops dwc3_gadget_ops = { .udc_start = dwc3_gadget_start, .udc_stop = dwc3_gadget_stop, .udc_set_speed = dwc3_gadget_set_speed, + .get_config_params = dwc3_gadget_config_params, }; /* -------------------------------------------------------------------------- */ @@ -2251,8 +2271,6 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum) dep->endpoint.comp_desc = NULL; } - spin_lock_init(&dep->lock); - if (num == 0) ret = dwc3_gadget_init_control_endpoint(dep); else if (direction) diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 3ed738e86ea7..5faf4d1249e0 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -48,6 +48,12 @@ struct dwc3; /* DEPXFERCFG parameter 0 */ #define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff) +/* U1 Device exit Latency */ +#define DWC3_DEFAULT_U1_DEV_EXIT_LAT 0x0A /* Less then 10 microsec */ + +/* U2 Device exit Latency */ +#define DWC3_DEFAULT_U2_DEV_EXIT_LAT 0x1FF /* Less then 511 microsec */ + /* -------------------------------------------------------------------------- */ #define to_dwc3_request(r) (container_of(r, struct dwc3_request, request)) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index b8a15840b4ff..9118b42c70b6 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -653,7 +653,7 @@ static int bos_desc(struct usb_composite_dev *cdev) /* Get Controller configuration */ if (cdev->gadget->ops->get_config_params) { - cdev->gadget->ops->get_config_params( + cdev->gadget->ops->get_config_params(cdev->gadget, &dcd_config_params); } else { dcd_config_params.bU1devExitLat = diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 47be961f1bf3..213ff03c8a9f 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -997,7 +997,6 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) * earlier */ gadget = epfile->ffs->gadget; - io_data->use_sg = gadget->sg_supported && data_len > PAGE_SIZE; spin_lock_irq(&epfile->ffs->eps_lock); /* In the meantime, endpoint got disabled or changed. */ @@ -1012,6 +1011,8 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) */ if (io_data->read) data_len = usb_ep_align_maybe(gadget, ep->ep, data_len); + + io_data->use_sg = gadget->sg_supported && data_len > PAGE_SIZE; spin_unlock_irq(&epfile->ffs->eps_lock); data = ffs_alloc_buffer(io_data, data_len); @@ -1182,11 +1183,12 @@ static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from) ENTER(); if (!is_sync_kiocb(kiocb)) { - p = kmalloc(sizeof(io_data), GFP_KERNEL); + p = kzalloc(sizeof(io_data), GFP_KERNEL); if (unlikely(!p)) return -ENOMEM; p->aio = true; } else { + memset(p, 0, sizeof(*p)); p->aio = false; } @@ -1218,11 +1220,12 @@ static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to) ENTER(); if (!is_sync_kiocb(kiocb)) { - p = kmalloc(sizeof(io_data), GFP_KERNEL); + p = kzalloc(sizeof(io_data), GFP_KERNEL); if (unlikely(!p)) return -ENOMEM; p->aio = true; } else { + memset(p, 0, sizeof(*p)); p->aio = false; } diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 29cc5693e05c..b1fba3132427 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -2293,8 +2293,7 @@ static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) static void fsg_disable(struct usb_function *f) { struct fsg_dev *fsg = fsg_from_func(f); - fsg->common->new_fsg = NULL; - raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); + raise_exception(fsg->common, FSG_STATE_DISCONNECT); } @@ -2307,6 +2306,7 @@ static void handle_exception(struct fsg_common *common) enum fsg_state old_state; struct fsg_lun *curlun; unsigned int exception_req_tag; + struct fsg_dev *fsg; /* * Clear the existing signals. Anything but SIGUSR1 is converted @@ -2413,9 +2413,19 @@ static void handle_exception(struct fsg_common *common) break; case FSG_STATE_CONFIG_CHANGE: - do_set_interface(common, common->new_fsg); - if (common->new_fsg) + fsg = common->new_fsg; + /* + * Add a check here to double confirm if a disconnect event + * occurs and common->new_fsg has been cleared. + */ + if (fsg) { + do_set_interface(common, fsg); usb_composite_setup_continue(common->cdev); + } + break; + + case FSG_STATE_DISCONNECT: + do_set_interface(common, NULL); break; case FSG_STATE_EXIT: @@ -2989,8 +2999,7 @@ static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) DBG(fsg, "unbind\n"); if (fsg->common->fsg == fsg) { - fsg->common->new_fsg = NULL; - raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); + raise_exception(fsg->common, FSG_STATE_DISCONNECT); /* FIXME: make interruptible or killable somehow? */ wait_event(common->fsg_wait, common->fsg != fsg); } diff --git a/drivers/usb/gadget/function/storage_common.h b/drivers/usb/gadget/function/storage_common.h index e5e3a2553aaa..12687f7e3de9 100644 --- a/drivers/usb/gadget/function/storage_common.h +++ b/drivers/usb/gadget/function/storage_common.h @@ -161,6 +161,7 @@ enum fsg_state { FSG_STATE_ABORT_BULK_OUT, FSG_STATE_PROTOCOL_RESET, FSG_STATE_CONFIG_CHANGE, + FSG_STATE_DISCONNECT, FSG_STATE_EXIT, FSG_STATE_TERMINATED }; diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index fb5ed97572e5..56906d15fb55 100644 --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -40,7 +40,7 @@ struct uac_rtd_params { void *rbuf; - unsigned max_psize; /* MaxPacketSize of endpoint */ + unsigned int max_psize; /* MaxPacketSize of endpoint */ struct uac_req *ureq; spinlock_t lock; @@ -78,7 +78,7 @@ static const struct snd_pcm_hardware uac_pcm_hardware = { static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req) { - unsigned pending; + unsigned int pending; unsigned long flags, flags2; unsigned int hw_ptr; int status = req->status; diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 737bd77a575d..fbe96ef1ac7a 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -186,11 +186,12 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags) out = dev->port_usb->out_ep; else out = NULL; - spin_unlock_irqrestore(&dev->lock, flags); if (!out) + { + spin_unlock_irqrestore(&dev->lock, flags); return -ENOTCONN; - + } /* Padding up to RX_EXTRA handles minor disagreements with host. * Normally we use the USB "terminate on short read" convention; @@ -214,6 +215,7 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags) if (dev->port_usb->is_fixed) size = max_t(size_t, size, dev->port_usb->fixed_out_len); + spin_unlock_irqrestore(&dev->lock, flags); skb = __netdev_alloc_skb(dev->net, size + NET_IP_ALIGN, gfp_flags); if (skb == NULL) { @@ -1004,9 +1006,9 @@ int gether_get_ifname(struct net_device *net, char *name, int len) int ret; rtnl_lock(); - ret = snprintf(name, len, "%s\n", netdev_name(net)); + ret = scnprintf(name, len, "%s\n", netdev_name(net)); rtnl_unlock(); - return ret < len ? ret : len; + return ret; } EXPORT_SYMBOL_GPL(gether_get_ifname); diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index 03959dc86cfd..194ffb1ed462 100644 --- a/drivers/usb/gadget/udc/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -799,7 +799,6 @@ static int at91_wakeup(struct usb_gadget *gadget) { struct at91_udc *udc = to_udc(gadget); u32 glbstate; - int status = -EINVAL; unsigned long flags; DBG("%s\n", __func__ ); @@ -818,7 +817,7 @@ static int at91_wakeup(struct usb_gadget *gadget) done: spin_unlock_irqrestore(&udc->lock, flags); - return status; + return 0; } /* reinit == restore initial software state */ diff --git a/drivers/usb/gadget/udc/fotg210-udc.c b/drivers/usb/gadget/udc/fotg210-udc.c index cec49294bac6..21f3e6c4e4d6 100644 --- a/drivers/usb/gadget/udc/fotg210-udc.c +++ b/drivers/usb/gadget/udc/fotg210-udc.c @@ -481,7 +481,6 @@ static int fotg210_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedge) struct fotg210_ep *ep; struct fotg210_udc *fotg210; unsigned long flags; - int ret = 0; ep = container_of(_ep, struct fotg210_ep, ep); @@ -504,7 +503,7 @@ static int fotg210_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedge) } spin_unlock_irqrestore(&ep->fotg210->lock, flags); - return ret; + return 0; } static int fotg210_ep_set_halt(struct usb_ep *_ep, int value) diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c index 564aeee1a1fe..247de0faaeb7 100644 --- a/drivers/usb/gadget/udc/net2272.c +++ b/drivers/usb/gadget/udc/net2272.c @@ -1178,11 +1178,6 @@ registers_show(struct device *_dev, struct device_attribute *attr, char *buf) size = PAGE_SIZE; spin_lock_irqsave(&dev->lock, flags); - if (dev->driver) - s = dev->driver->driver.name; - else - s = "(none)"; - /* Main Control Registers */ t = scnprintf(next, size, "%s version %s," "chiprev %02x, locctl %02x\n" diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c index fcf13ef33b31..f36f0730afab 100644 --- a/drivers/usb/gadget/udc/omap_udc.c +++ b/drivers/usb/gadget/udc/omap_udc.c @@ -2103,7 +2103,6 @@ done: static int omap_udc_stop(struct usb_gadget *g) { unsigned long flags; - int status = -ENODEV; if (udc->dc_clk != NULL) omap_udc_enable_clock(1); @@ -2125,7 +2124,7 @@ static int omap_udc_stop(struct usb_gadget *g) if (udc->dc_clk != NULL) omap_udc_enable_clock(0); - return status; + return 0; } /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index 7dc248546fd4..5a960fce31c5 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -351,6 +351,8 @@ struct renesas_usb3 { int disabled_count; struct usb_request *ep0_req; + + enum usb_role connection_state; u16 test_mode; u8 ep0_buf[USB3_EP0_BUF_SIZE]; bool softconnect; @@ -359,6 +361,7 @@ struct renesas_usb3 { bool extcon_usb; /* check vbus and set EXTCON_USB */ bool forced_b_device; bool start_to_connect; + bool role_sw_by_connector; }; #define gadget_to_renesas_usb3(_gadget) \ @@ -699,8 +702,11 @@ static void usb3_mode_config(struct renesas_usb3 *usb3, bool host, bool a_dev) unsigned long flags; spin_lock_irqsave(&usb3->lock, flags); - usb3_set_mode_by_role_sw(usb3, host); - usb3_vbus_out(usb3, a_dev); + if (!usb3->role_sw_by_connector || + usb3->connection_state != USB_ROLE_NONE) { + usb3_set_mode_by_role_sw(usb3, host); + usb3_vbus_out(usb3, a_dev); + } /* for A-Peripheral or forced B-device mode */ if ((!host && a_dev) || usb3->start_to_connect) usb3_connect(usb3); @@ -716,7 +722,8 @@ static void usb3_check_id(struct renesas_usb3 *usb3) { usb3->extcon_host = usb3_is_a_device(usb3); - if (usb3->extcon_host && !usb3->forced_b_device) + if ((!usb3->role_sw_by_connector && usb3->extcon_host && + !usb3->forced_b_device) || usb3->connection_state == USB_ROLE_HOST) usb3_mode_config(usb3, true, true); else usb3_mode_config(usb3, false, false); @@ -2343,14 +2350,65 @@ static enum usb_role renesas_usb3_role_switch_get(struct device *dev) return cur_role; } -static int renesas_usb3_role_switch_set(struct device *dev, - enum usb_role role) +static void handle_ext_role_switch_states(struct device *dev, + enum usb_role role) +{ + struct renesas_usb3 *usb3 = dev_get_drvdata(dev); + struct device *host = usb3->host_dev; + enum usb_role cur_role = renesas_usb3_role_switch_get(dev); + + switch (role) { + case USB_ROLE_NONE: + usb3->connection_state = USB_ROLE_NONE; + if (usb3->driver) + usb3_disconnect(usb3); + usb3_vbus_out(usb3, false); + break; + case USB_ROLE_DEVICE: + if (usb3->connection_state == USB_ROLE_NONE) { + usb3->connection_state = USB_ROLE_DEVICE; + usb3_set_mode(usb3, false); + if (usb3->driver) + usb3_connect(usb3); + } else if (cur_role == USB_ROLE_HOST) { + device_release_driver(host); + usb3_set_mode(usb3, false); + if (usb3->driver) + usb3_connect(usb3); + } + usb3_vbus_out(usb3, false); + break; + case USB_ROLE_HOST: + if (usb3->connection_state == USB_ROLE_NONE) { + if (usb3->driver) + usb3_disconnect(usb3); + + usb3->connection_state = USB_ROLE_HOST; + usb3_set_mode(usb3, true); + usb3_vbus_out(usb3, true); + if (device_attach(host) < 0) + dev_err(dev, "device_attach(host) failed\n"); + } else if (cur_role == USB_ROLE_DEVICE) { + usb3_disconnect(usb3); + /* Must set the mode before device_attach of the host */ + usb3_set_mode(usb3, true); + /* This device_attach() might sleep */ + if (device_attach(host) < 0) + dev_err(dev, "device_attach(host) failed\n"); + } + break; + default: + break; + } +} + +static void handle_role_switch_states(struct device *dev, + enum usb_role role) { struct renesas_usb3 *usb3 = dev_get_drvdata(dev); struct device *host = usb3->host_dev; enum usb_role cur_role = renesas_usb3_role_switch_get(dev); - pm_runtime_get_sync(dev); if (cur_role == USB_ROLE_HOST && role == USB_ROLE_DEVICE) { device_release_driver(host); usb3_set_mode(usb3, false); @@ -2361,6 +2419,20 @@ static int renesas_usb3_role_switch_set(struct device *dev, if (device_attach(host) < 0) dev_err(dev, "device_attach(host) failed\n"); } +} + +static int renesas_usb3_role_switch_set(struct device *dev, + enum usb_role role) +{ + struct renesas_usb3 *usb3 = dev_get_drvdata(dev); + + pm_runtime_get_sync(dev); + + if (usb3->role_sw_by_connector) + handle_ext_role_switch_states(dev, role); + else + handle_role_switch_states(dev, role); + pm_runtime_put(dev); return 0; @@ -2650,7 +2722,7 @@ static const unsigned int renesas_usb3_cable[] = { EXTCON_NONE, }; -static const struct usb_role_switch_desc renesas_usb3_role_switch_desc = { +static struct usb_role_switch_desc renesas_usb3_role_switch_desc = { .set = renesas_usb3_role_switch_set, .get = renesas_usb3_role_switch_get, .allow_userspace_control = true, @@ -2741,6 +2813,11 @@ static int renesas_usb3_probe(struct platform_device *pdev) if (ret < 0) goto err_dev_create; + if (device_property_read_bool(&pdev->dev, "usb-role-switch")) { + usb3->role_sw_by_connector = true; + renesas_usb3_role_switch_desc.fwnode = dev_fwnode(&pdev->dev); + } + INIT_WORK(&usb3->role_work, renesas_usb3_role_work); usb3->role_sw = usb_role_switch_register(&pdev->dev, &renesas_usb3_role_switch_desc); diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 7595056b96c1..fb19141151d8 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -310,7 +310,8 @@ struct usb_gadget_ops { int (*pullup) (struct usb_gadget *, int is_on); int (*ioctl)(struct usb_gadget *, unsigned code, unsigned long param); - void (*get_config_params)(struct usb_dcd_config_params *); + void (*get_config_params)(struct usb_gadget *, + struct usb_dcd_config_params *); int (*udc_start)(struct usb_gadget *, struct usb_gadget_driver *); int (*udc_stop)(struct usb_gadget *); |