[PATCH v2 4/4] usb: dwc3: Enable frame number tracking based on reference clock

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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 <thinhn@xxxxxxxxxxxx>
---
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




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux