diff options
Diffstat (limited to 'drivers/usb/dwc3')
-rw-r--r-- | drivers/usb/dwc3/core.c | 39 | ||||
-rw-r--r-- | drivers/usb/dwc3/core.h | 3 |
2 files changed, 41 insertions, 1 deletions
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 38fef5c74359..18adddfba3da 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -348,6 +348,8 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc) static void dwc3_ref_clk_period(struct dwc3 *dwc) { unsigned long period; + unsigned long fladj; + unsigned long decr; unsigned long rate; u32 reg; @@ -358,6 +360,7 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc) period = NSEC_PER_SEC / rate; } else if (dwc->ref_clk_per) { period = dwc->ref_clk_per; + rate = NSEC_PER_SEC / period; } else { return; } @@ -366,8 +369,42 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc) reg &= ~DWC3_GUCTL_REFCLKPER_MASK; reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, period); dwc3_writel(dwc->regs, DWC3_GUCTL, reg); -} + if (DWC3_VER_IS_PRIOR(DWC3, 250A)) + return; + + /* + * The calculation below is + * + * 125000 * (NSEC_PER_SEC / (rate * period) - 1) + * + * but rearranged for fixed-point arithmetic. The division must be + * 64-bit because 125000 * NSEC_PER_SEC doesn't fit in 32 bits (and + * neither does rate * period). + * + * Note that rate * period ~= NSEC_PER_SECOND, minus the number of + * nanoseconds of error caused by the truncation which happened during + * the division when calculating rate or period (whichever one was + * derived from the other). We first calculate the relative error, then + * scale it to units of 8 ppm. + */ + fladj = div64_u64(125000ULL * NSEC_PER_SEC, (u64)rate * period); + fladj -= 125000; + + /* + * The documented 240MHz constant is scaled by 2 to get PLS1 as well. + */ + decr = 480000000 / rate; + + reg = dwc3_readl(dwc->regs, DWC3_GFLADJ); + reg &= ~DWC3_GFLADJ_REFCLK_FLADJ_MASK + & ~DWC3_GFLADJ_240MHZDECR + & ~DWC3_GFLADJ_240MHZDECR_PLS1; + reg |= FIELD_PREP(DWC3_GFLADJ_REFCLK_FLADJ_MASK, fladj) + | FIELD_PREP(DWC3_GFLADJ_240MHZDECR, decr >> 1) + | FIELD_PREP(DWC3_GFLADJ_240MHZDECR_PLS1, decr & 1); + dwc3_writel(dwc->regs, DWC3_GFLADJ, reg); +} /** * dwc3_free_one_event_buffer - Frees one event buffer diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 45cfa7d9f27a..eb9c1efced05 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -388,6 +388,9 @@ /* Global Frame Length Adjustment Register */ #define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7) #define DWC3_GFLADJ_30MHZ_MASK 0x3f +#define DWC3_GFLADJ_REFCLK_FLADJ_MASK GENMASK(21, 8) +#define DWC3_GFLADJ_240MHZDECR GENMASK(30, 24) +#define DWC3_GFLADJ_240MHZDECR_PLS1 BIT(31) /* Global User Control Register*/ #define DWC3_GUCTL_REFCLKPER_MASK 0xffc00000 |