From fc74bada7f6940619f8930556b7c61fe618f2a64 Mon Sep 17 00:00:00 2001 From: Romuald JEANNE Date: Fri, 21 Dec 2018 16:56:27 +0100 Subject: [PATCH 50/52] ARM: stm32mp1-r0-rc4: USB --- Documentation/devicetree/bindings/usb/dwc2.txt | 2 + drivers/usb/dwc2/core.h | 11 ++++ drivers/usb/dwc2/core_intr.c | 4 +- drivers/usb/dwc2/hw.h | 2 + drivers/usb/dwc2/params.c | 7 ++- drivers/usb/dwc2/platform.c | 74 +++++++++++++++++++++++--- 6 files changed, 90 insertions(+), 10 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/dwc2.txt b/Documentation/devicetree/bindings/usb/dwc2.txt index 32b245c..03c62de 100644 --- a/Documentation/devicetree/bindings/usb/dwc2.txt +++ b/Documentation/devicetree/bindings/usb/dwc2.txt @@ -43,6 +43,8 @@ Refer to phy/phy-bindings.txt for generic phy consumer properties doesn't drive it. - usb33d-supply: external VBUS and ID sensing comparators supply, in order to perform OTG operation, used on STM32MP1 SoCs. +- force-b-session-valid: force B-peripheral session instead of relying on + VBUS sensing (only valid when dr_mode = "peripheral"). Deprecated properties: - g-use-dma: gadget DMA mode is automatically detected diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 4c689d1..4c1736f 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -415,6 +415,12 @@ enum dwc2_ep0_state { * in DWORDS with possible values from from * 16-32768 (default: 256, 256, 256, 256, 768, * 768, 768, 768, 0, 0, 0, 0, 0, 0, 0). + * @force_b_session_valid: force B-peripheral session instead of relying on + * VBUS sensing (only valid when dr_mode = "peripheral"). + * @suspend_ignore_power_down: prevent the controller to enter low power mode + * upon suspend interrupt. This may help in device mode, + * when suspend (3ms idle bus) gets detected before + * device session end (VBUS discharge > 3ms). * @change_speed_quirk: Change speed configuration to DWC2_SPEED_PARAM_FULL * while full&low speed device connect. And change speed * back to DWC2_SPEED_PARAM_HIGH while device is gone. @@ -492,6 +498,8 @@ struct dwc2_core_params { u32 g_rx_fifo_size; u32 g_np_tx_fifo_size; u32 g_tx_fifo_size[MAX_EPS_CHANNELS]; + bool force_b_session_valid; + bool suspend_ignore_power_down; bool change_speed_quirk; }; @@ -849,6 +857,8 @@ struct dwc2_hregs_backup { * removed once all SoCs support usb transceiver. * @supplies: Definition of USB power supplies * @vbus_supply: Regulator supplying vbus. + * @usb33d: Optional 3.3v regulator used on some stm32 devices to + * supply ID and VBUS detection hardware. * @phyif: PHY interface width * @lock: Spinlock that protects all the driver data structures * @priv: Stores a pointer to the struct usb_hcd @@ -1032,6 +1042,7 @@ struct dwc2_hsotg { struct dwc2_hsotg_plat *plat; struct regulator_bulk_data supplies[DWC2_NUM_SUPPLIES]; struct regulator *vbus_supply; + struct regulator *usb33d; u32 phyif; spinlock_t lock; diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 19ae259..7b4162c 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -492,7 +492,9 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) hsotg->hw_params.hibernation); /* Ignore suspend request before enumeration */ - if (!dwc2_is_device_connected(hsotg)) { + if (!dwc2_is_device_connected(hsotg) || + hsotg->params.force_b_session_valid || + hsotg->params.suspend_ignore_power_down) { dev_dbg(hsotg->dev, "ignore suspend request before enumeration\n"); return; diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h index afde335..31f8c60 100644 --- a/drivers/usb/dwc2/hw.h +++ b/drivers/usb/dwc2/hw.h @@ -54,6 +54,8 @@ #define GOTGCTL_HSTSETHNPEN BIT(10) #define GOTGCTL_HNPREQ BIT(9) #define GOTGCTL_HSTNEGSCS BIT(8) +#define GOTGCTL_BVALOVAL BIT(7) +#define GOTGCTL_BVALOEN BIT(6) #define GOTGCTL_SESREQ BIT(1) #define GOTGCTL_SESREQSCS BIT(0) diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index 63ccfc9..7fef905 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -167,7 +167,7 @@ static void dwc2_set_stm32mp1_hsotg_params(struct dwc2_hsotg *hsotg) p->host_rx_fifo_size = 440; p->host_nperio_tx_fifo_size = 256; p->host_perio_tx_fifo_size = 256; - p->power_down = false; + p->suspend_ignore_power_down = true; } const struct of_device_id dwc2_of_match_table[] = { @@ -404,6 +404,11 @@ static void dwc2_get_device_properties(struct dwc2_hsotg *hsotg) if (of_find_property(hsotg->dev->of_node, "disable-over-current", NULL)) p->oc_disable = true; + + if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) + p->force_b_session_valid = + of_property_read_bool(hsotg->dev->of_node, + "force-b-session-valid"); } static void dwc2_check_param_otg_cap(struct dwc2_hsotg *hsotg) diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 2061254..b2e5ddc 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -324,6 +324,10 @@ static int dwc2_driver_remove(struct platform_device *dev) if (hsotg->gadget_enabled) dwc2_hsotg_remove(hsotg); + if (hsotg->params.activate_stm_id_vb_detection && + !hsotg->params.force_b_session_valid) + regulator_disable(hsotg->usb33d); + if (hsotg->ll_hw_enabled) dwc2_lowlevel_hw_disable(hsotg); @@ -474,18 +478,18 @@ static int dwc2_driver_probe(struct platform_device *dev) if (retval) goto error; - if (hsotg->params.activate_stm_id_vb_detection) { - struct regulator *usb33d; + if (hsotg->params.activate_stm_id_vb_detection && + !hsotg->params.force_b_session_valid) { u32 ggpio; - usb33d = devm_regulator_get(hsotg->dev, "usb33d"); - if (IS_ERR(usb33d)) { - retval = PTR_ERR(usb33d); + hsotg->usb33d = devm_regulator_get(hsotg->dev, "usb33d"); + if (IS_ERR(hsotg->usb33d)) { + retval = PTR_ERR(hsotg->usb33d); dev_err(hsotg->dev, "can't get voltage level detector supply\n"); goto error; } - retval = regulator_enable(usb33d); + retval = regulator_enable(hsotg->usb33d); if (retval) { dev_err(hsotg->dev, "can't enable voltage level detector supply\n"); @@ -498,6 +502,15 @@ static int dwc2_driver_probe(struct platform_device *dev) dwc2_writel(hsotg, ggpio, GGPIO); } + if (hsotg->params.force_b_session_valid) { + u32 gotgctl; + + gotgctl = dwc2_readl(hsotg, GOTGCTL); + gotgctl |= GOTGCTL_BVALOVAL; /* B-peripheral session value */ + gotgctl |= GOTGCTL_BVALOEN; /* B-peripheral override enable */ + dwc2_writel(hsotg, gotgctl, GOTGCTL); + } + if (hsotg->params.activate_stm_fs_transceiver) { u32 ggpio; @@ -516,7 +529,7 @@ static int dwc2_driver_probe(struct platform_device *dev) if (hsotg->dr_mode != USB_DR_MODE_HOST) { retval = dwc2_gadget_init(hsotg); if (retval) - goto error; + goto error_init; hsotg->gadget_enabled = 1; } @@ -525,7 +538,7 @@ static int dwc2_driver_probe(struct platform_device *dev) if (retval) { if (hsotg->gadget_enabled) dwc2_hsotg_remove(hsotg); - goto error; + goto error_init; } hsotg->hcd_enabled = 1; } @@ -541,6 +554,10 @@ static int dwc2_driver_probe(struct platform_device *dev) return 0; +error_init: + if (hsotg->params.activate_stm_id_vb_detection && + !hsotg->params.force_b_session_valid) + regulator_disable(hsotg->usb33d); error: dwc2_lowlevel_hw_disable(hsotg); return retval; @@ -554,6 +571,18 @@ static int __maybe_unused dwc2_suspend(struct device *dev) if (dwc2_is_device_mode(dwc2)) dwc2_hsotg_suspend(dwc2); + if (dwc2->params.activate_stm_id_vb_detection && + !dwc2->params.force_b_session_valid) { + u32 ggpio; + + ggpio = dwc2_readl(dwc2, GGPIO); + ggpio &= ~GGPIO_STM32_OTG_GCCFG_IDEN; + ggpio &= ~GGPIO_STM32_OTG_GCCFG_VBDEN; + dwc2_writel(dwc2, ggpio, GGPIO); + + regulator_disable(dwc2->usb33d); + } + if (dwc2->ll_hw_enabled) ret = __dwc2_lowlevel_hw_disable(dwc2); @@ -571,6 +600,35 @@ static int __maybe_unused dwc2_resume(struct device *dev) return ret; } + /* Need to restore FORCEDEVMODE/FORCEHOSTMODE */ + dwc2_force_dr_mode(dwc2); + + if (dwc2->params.activate_stm_id_vb_detection && + !dwc2->params.force_b_session_valid) { + u32 ggpio; + + ret = regulator_enable(dwc2->usb33d); + if (ret) + return ret; + + ggpio = dwc2_readl(dwc2, GGPIO); + ggpio |= GGPIO_STM32_OTG_GCCFG_IDEN; + ggpio |= GGPIO_STM32_OTG_GCCFG_VBDEN; + dwc2_writel(dwc2, ggpio, GGPIO); + + /* ID/VBUS detection startup time */ + usleep_range(5000, 7000); + } + + if (dwc2->params.force_b_session_valid) { + u32 gotgctl; + + gotgctl = dwc2_readl(dwc2, GOTGCTL); + gotgctl |= GOTGCTL_BVALOVAL; /* B-peripheral session value */ + gotgctl |= GOTGCTL_BVALOEN; /* B-peripheral override enable */ + dwc2_writel(dwc2, gotgctl, GOTGCTL); + } + if (dwc2_is_device_mode(dwc2)) ret = dwc2_hsotg_resume(dwc2); -- 2.7.4