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