OSDN Git Service

usb: dwc2: add support for STM32MP15 SoCs USB OTG HS and FS
authorAmelie Delaunay <amelie.delaunay@st.com>
Fri, 24 Jan 2020 08:41:31 +0000 (09:41 +0100)
committerFelipe Balbi <balbi@kernel.org>
Sun, 15 Mar 2020 09:06:39 +0000 (11:06 +0200)
This patch introduces a new parameter to activate external ID pin and valid
vbus level detection, required on STM32MP15 SoC to support dual role,
either in HS or FS.
The STM32MP15 SoC uses the GGPIO register to enable the level detection.
The level detector requires to be powered.
Also adds the params structures for STM32MP15 OTG HS and STM32MP1 OTG FS.

Acked-by: Minas Harutyunyan <hminas@synopsys.com>
Signed-off-by: Amelie Delaunay <amelie.delaunay@st.com>
Signed-off-by: Felipe Balbi <balbi@kernel.org>
drivers/usb/dwc2/core.h
drivers/usb/dwc2/hw.h
drivers/usb/dwc2/params.c
drivers/usb/dwc2/platform.c

index 968e03b..99b0bdf 100644 (file)
@@ -411,6 +411,10 @@ enum dwc2_ep0_state {
  *                     register.
  *                     0 - Deactivate the transceiver (default)
  *                     1 - Activate the transceiver
+ * @activate_stm_id_vb_detection: Activate external ID pin and Vbus level
+ *                     detection using GGPIO register.
+ *                     0 - Deactivate the external level detection (default)
+ *                     1 - Activate the external level detection
  * @g_dma:              Enables gadget dma usage (default: autodetect).
  * @g_dma_desc:         Enables gadget descriptor DMA (default: autodetect).
  * @g_rx_fifo_size:    The periodic rx fifo size for the device, in
@@ -481,6 +485,7 @@ struct dwc2_core_params {
        bool service_interval;
        u8 hird_threshold;
        bool activate_stm_fs_transceiver;
+       bool activate_stm_id_vb_detection;
        bool ipg_isoc_en;
        u16 max_packet_count;
        u32 max_transfer_size;
@@ -874,6 +879,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.
  * @lock:              Spinlock that protects all the driver data structures
  * @priv:              Stores a pointer to the struct usb_hcd
  * @queuing_high_bandwidth: True if multiple packets of a high-bandwidth
@@ -1061,6 +1068,7 @@ struct dwc2_hsotg {
        struct dwc2_hsotg_plat *plat;
        struct regulator_bulk_data supplies[DWC2_NUM_SUPPLIES];
        struct regulator *vbus_supply;
+       struct regulator *usb33d;
 
        spinlock_t lock;
        void *priv;
index 510e87e..c4027bb 100644 (file)
 #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_AVALOVAL               BIT(5)
+#define GOTGCTL_AVALOEN                        BIT(4)
+#define GOTGCTL_VBVALOVAL              BIT(3)
+#define GOTGCTL_VBVALOEN               BIT(2)
 #define GOTGCTL_SESREQ                 BIT(1)
 #define GOTGCTL_SESREQSCS              BIT(0)
 
 #define GPVNDCTL                       HSOTG_REG(0x0034)
 #define GGPIO                          HSOTG_REG(0x0038)
 #define GGPIO_STM32_OTG_GCCFG_PWRDWN   BIT(16)
+#define GGPIO_STM32_OTG_GCCFG_VBDEN    BIT(21)
+#define GGPIO_STM32_OTG_GCCFG_IDEN     BIT(22)
 
 #define GUID                           HSOTG_REG(0x003c)
 #define GSNPSID                                HSOTG_REG(0x0040)
index 31e090a..8ccc83f 100644 (file)
@@ -163,6 +163,35 @@ static void dwc2_set_stm32f7_hsotg_params(struct dwc2_hsotg *hsotg)
        p->host_perio_tx_fifo_size = 256;
 }
 
+static void dwc2_set_stm32mp15_fsotg_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
+       p->speed = DWC2_SPEED_PARAM_FULL;
+       p->host_rx_fifo_size = 128;
+       p->host_nperio_tx_fifo_size = 96;
+       p->host_perio_tx_fifo_size = 96;
+       p->max_packet_count = 256;
+       p->phy_type = DWC2_PHY_TYPE_PARAM_FS;
+       p->i2c_enable = false;
+       p->activate_stm_fs_transceiver = true;
+       p->activate_stm_id_vb_detection = true;
+       p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
+}
+
+static void dwc2_set_stm32mp15_hsotg_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
+       p->activate_stm_id_vb_detection = true;
+       p->host_rx_fifo_size = 440;
+       p->host_nperio_tx_fifo_size = 256;
+       p->host_perio_tx_fifo_size = 256;
+       p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
+}
+
 const struct of_device_id dwc2_of_match_table[] = {
        { .compatible = "brcm,bcm2835-usb", .data = dwc2_set_bcm_params },
        { .compatible = "hisilicon,hi6220-usb", .data = dwc2_set_his_params  },
@@ -186,6 +215,10 @@ const struct of_device_id dwc2_of_match_table[] = {
        { .compatible = "st,stm32f4x9-hsotg" },
        { .compatible = "st,stm32f7-hsotg",
          .data = dwc2_set_stm32f7_hsotg_params },
+       { .compatible = "st,stm32mp15-fsotg",
+         .data = dwc2_set_stm32mp15_fsotg_params },
+       { .compatible = "st,stm32mp15-hsotg",
+         .data = dwc2_set_stm32mp15_hsotg_params },
        {},
 };
 MODULE_DEVICE_TABLE(of, dwc2_of_match_table);
index 3c6ce09..8368d6d 100644 (file)
@@ -312,6 +312,9 @@ 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)
+               regulator_disable(hsotg->usb33d);
+
        if (hsotg->ll_hw_enabled)
                dwc2_lowlevel_hw_disable(hsotg);
 
@@ -464,10 +467,35 @@ static int dwc2_driver_probe(struct platform_device *dev)
        if (retval)
                goto error;
 
+       if (hsotg->params.activate_stm_id_vb_detection) {
+               u32 ggpio;
+
+               hsotg->usb33d = devm_regulator_get(hsotg->dev, "usb33d");
+               if (IS_ERR(hsotg->usb33d)) {
+                       retval = PTR_ERR(hsotg->usb33d);
+                       if (retval != -EPROBE_DEFER)
+                               dev_err(hsotg->dev,
+                                       "failed to request usb33d supply: %d\n",
+                                       retval);
+                       goto error;
+               }
+               retval = regulator_enable(hsotg->usb33d);
+               if (retval) {
+                       dev_err(hsotg->dev,
+                               "failed to enable usb33d supply: %d\n", retval);
+                       goto error;
+               }
+
+               ggpio = dwc2_readl(hsotg, GGPIO);
+               ggpio |= GGPIO_STM32_OTG_GCCFG_IDEN;
+               ggpio |= GGPIO_STM32_OTG_GCCFG_VBDEN;
+               dwc2_writel(hsotg, ggpio, GGPIO);
+       }
+
        if (hsotg->dr_mode != USB_DR_MODE_HOST) {
                retval = dwc2_gadget_init(hsotg);
                if (retval)
-                       goto error;
+                       goto error_init;
                hsotg->gadget_enabled = 1;
        }
 
@@ -493,7 +521,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;
        }
@@ -509,6 +537,9 @@ static int dwc2_driver_probe(struct platform_device *dev)
 
        return 0;
 
+error_init:
+       if (hsotg->params.activate_stm_id_vb_detection)
+               regulator_disable(hsotg->usb33d);
 error:
        dwc2_lowlevel_hw_disable(hsotg);
        return retval;
@@ -523,6 +554,37 @@ static int __maybe_unused dwc2_suspend(struct device *dev)
        if (is_device_mode)
                dwc2_hsotg_suspend(dwc2);
 
+       if (dwc2->params.activate_stm_id_vb_detection) {
+               unsigned long flags;
+               u32 ggpio, gotgctl;
+
+               /*
+                * Need to force the mode to the current mode to avoid Mode
+                * Mismatch Interrupt when ID detection will be disabled.
+                */
+               dwc2_force_mode(dwc2, !is_device_mode);
+
+               spin_lock_irqsave(&dwc2->lock, flags);
+               gotgctl = dwc2_readl(dwc2, GOTGCTL);
+               /* bypass debounce filter, enable overrides */
+               gotgctl |= GOTGCTL_DBNCE_FLTR_BYPASS;
+               gotgctl |= GOTGCTL_BVALOEN | GOTGCTL_AVALOEN;
+               /* Force A / B session if needed */
+               if (gotgctl & GOTGCTL_ASESVLD)
+                       gotgctl |= GOTGCTL_AVALOVAL;
+               if (gotgctl & GOTGCTL_BSESVLD)
+                       gotgctl |= GOTGCTL_BVALOVAL;
+               dwc2_writel(dwc2, gotgctl, GOTGCTL);
+               spin_unlock_irqrestore(&dwc2->lock, flags);
+
+               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 &&
            (is_device_mode || dwc2_host_can_poweroff_phy(dwc2))) {
                ret = __dwc2_lowlevel_hw_disable(dwc2);
@@ -544,6 +606,34 @@ static int __maybe_unused dwc2_resume(struct device *dev)
        }
        dwc2->phy_off_for_suspend = false;
 
+       if (dwc2->params.activate_stm_id_vb_detection) {
+               unsigned long flags;
+               u32 ggpio, gotgctl;
+
+               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);
+
+               spin_lock_irqsave(&dwc2->lock, flags);
+               gotgctl = dwc2_readl(dwc2, GOTGCTL);
+               gotgctl &= ~GOTGCTL_DBNCE_FLTR_BYPASS;
+               gotgctl &= ~(GOTGCTL_BVALOEN | GOTGCTL_AVALOEN |
+                            GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL);
+               dwc2_writel(dwc2, gotgctl, GOTGCTL);
+               spin_unlock_irqrestore(&dwc2->lock, flags);
+       }
+
+       /* Need to restore FORCEDEVMODE/FORCEHOSTMODE */
+       dwc2_force_dr_mode(dwc2);
+
        if (dwc2_is_device_mode(dwc2))
                ret = dwc2_hsotg_resume(dwc2);