Version 1.80a of the DWC_usb31 peripheral controller introduced a
feature to track the frame number based the reference clock. This patch
checks and enables this feature.

When operating in USB 2.0 mode, the peripheral controller uses the USB2
PHY clocks to track the frame number. This prevents the controller from
suspending the USB2 PHY when the device goes into low power. This
feature allows the controller to suspend the USB2 PHY when the device
enters low power. This improves power saving for devices that have
isochronous endpoints.

Signed-off-by: Thinh Nguyen <thi...@synopsys.com>
---
Changes in v2:
- Revise commit message
- Properly check for version and controller type

 drivers/usb/dwc3/core.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/dwc3/core.h | 12 ++++++++++++
 2 files changed, 63 insertions(+)

diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 32c38f71f874..38597a32cb20 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -882,6 +882,39 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
        dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, cfg);
 }
 
+/**
+ * dwc3_enable_refclk_sof - Enable frame number tracking based on ref_clk
+ * @dwc: Pointer to our controller context structure
+ *
+ * Returns 0 on success, otherwise negative errno.
+ */
+static int dwc3_enable_refclk_sof(struct dwc3 *dwc)
+{
+       u8              refclk_period_ns;
+       u32             reg;
+
+       reg = dwc3_readl(dwc->regs, DWC3_GUCTL);
+       refclk_period_ns = DWC3_GUCTL_GET_REFCLKPER(reg);
+
+       /* Only valid for the following reference clock periods */
+       switch (refclk_period_ns) {
+       case DWC3_GUCTL_REFCLKPER_25NS:
+       case DWC3_GUCTL_REFCLKPER_41NS:
+       case DWC3_GUCTL_REFCLKPER_50NS:
+       case DWC3_GUCTL_REFCLKPER_52NS:
+       case DWC3_GUCTL_REFCLKPER_58NS:
+       case DWC3_GUCTL_REFCLKPER_62NS:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
+       reg |= DWC3_GFLADJ_REFCLK_FLADJ;
+       dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
+       return 0;
+}
+
 /**
  * dwc3_core_init - Low-level initialization of DWC3 Core
  * @dwc: Pointer to our controller context structure
@@ -969,6 +1002,22 @@ static int dwc3_core_init(struct dwc3 *dwc)
                dwc3_writel(dwc->regs, DWC3_GUCTL, reg);
        }
 
+       /*
+        * For peripheral controller, frame number tracking based on reference
+        * clock is only introduced after DWC_usb31 version 1.80a.
+        */
+       if (dwc->enable_refclk_sof &&
+           (dwc->dr_mode != USB_DR_MODE_PERIPHERAL ||
+            (dwc->dr_mode == USB_DR_MODE_PERIPHERAL &&
+             dwc->revision >= DWC3_USB31_REVISION_180A))) {
+               ret = dwc3_enable_refclk_sof(dwc);
+               if (ret) {
+                       dev_err(dwc->dev,
+                               "can't enable ref_clk frame tracking\n");
+                       goto err4;
+               }
+       }
+
        /*
         * ENDXFER polling is available on version 3.10a and later of
         * the DWC_usb3 controller. It is NOT available in the
@@ -1261,6 +1310,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
                                "snps,usb3_lpm_capable");
        dwc->usb2_lpm_disable = device_property_read_bool(dev,
                                "snps,usb2-lpm-disable");
+       dwc->enable_refclk_sof = device_property_read_bool(dev,
+                               "snps,enable-refclk-sof");
        device_property_read_u8(dev, "snps,refclk-period-ns",
                                &dwc->refclk_period_ns);
        device_property_read_u8(dev, "snps,rx-thr-num-pkt-prd",
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index e190728104e0..dae2f918a932 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -248,6 +248,14 @@
 /* Global User Control Register */
 #define DWC3_GUCTL_HSTINAUTORETRY      BIT(14)
 #define DWC3_GUCTL_REFCLKPER(n)                (((n) & 0x3ff) << 22)
+#define DWC3_GUCTL_GET_REFCLKPER(n)    (((n) & (0x3ff << 22)) >> 22)
+
+#define DWC3_GUCTL_REFCLKPER_25NS      25
+#define DWC3_GUCTL_REFCLKPER_41NS      41
+#define DWC3_GUCTL_REFCLKPER_50NS      50
+#define DWC3_GUCTL_REFCLKPER_52NS      52
+#define DWC3_GUCTL_REFCLKPER_58NS      58
+#define DWC3_GUCTL_REFCLKPER_62NS      62
 
 /* Global User Control 1 Register */
 #define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS     BIT(28)
@@ -365,6 +373,7 @@
 #define DWC3_GHWPARAMS7_RAM2_DEPTH(n)  (((n) >> 16) & 0xffff)
 
 /* Global Frame Length Adjustment Register */
+#define DWC3_GFLADJ_REFCLK_FLADJ               BIT(23)
 #define DWC3_GFLADJ_30MHZ_SDBND_SEL            BIT(7)
 #define DWC3_GFLADJ_30MHZ_MASK                 0x3f
 
@@ -1020,6 +1029,7 @@ struct dwc3_scratchpad_array {
  *                     check during HS transmit.
  * @refclk_period_ns: if set, inform the controller this value as the reference
  *                     clock period in nanoseconds.
+ * @enable_refclk_sof: set to enable frame number tracking based on the ref_clk
  * @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
  * @tx_de_emphasis: Tx de-emphasis value
  *     0       - -6dB de-emphasis
@@ -1135,6 +1145,7 @@ struct dwc3 {
 #define DWC3_USB31_REVISION_120A       (0x3132302a | DWC3_REVISION_IS_DWC31)
 #define DWC3_USB31_REVISION_160A       (0x3136302a | DWC3_REVISION_IS_DWC31)
 #define DWC3_USB31_REVISION_170A       (0x3137302a | DWC3_REVISION_IS_DWC31)
+#define DWC3_USB31_REVISION_180A       (0x3138302a | DWC3_REVISION_IS_DWC31)
 
        u32                     version_type;
 
@@ -1192,6 +1203,7 @@ struct dwc3 {
        unsigned                dis_start_transfer_quirk:1;
        unsigned                usb3_lpm_capable:1;
        unsigned                usb2_lpm_disable:1;
+       unsigned                enable_refclk_sof:1;
 
        unsigned                disable_scramble_quirk:1;
        unsigned                u2exit_lfps_quirk:1;
-- 
2.11.0

Reply via email to