Skip to content

Commit

Permalink
LF-11213-1 usb: xhci-imx8m: Fix reference clock period issue
Browse files Browse the repository at this point in the history
Because iMX8MQ is using 100Mhz ref_clk for USB core, configured the period
parameters for 24Mhz ref_clk causes issue to iMX8MQ.
Change the codes to follow kernel method to parse the dts property
"snps,gfladj-refclk-lpm-sel-quirk" to enable GFLADJ_REFCLK_LPM_SEL, and
get ref clock rate to calculate the period parameters for GUCTL and GFLADJ

Signed-off-by: Ye Li <ye.li@nxp.com>
Reviewed-by: Jun Li <jun.li@nxp.com>
  • Loading branch information
Ye Li committed Jan 5, 2024
1 parent 708942f commit 4c1cf0a
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 10 deletions.
83 changes: 73 additions & 10 deletions drivers/usb/host/xhci-imx8m.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include <linux/errno.h>
#include <linux/compat.h>
#include <linux/usb/dwc3.h>
#include <linux/bitfield.h>
#include <linux/math64.h>
#include <asm/arch/sys_proto.h>
#include <dm.h>
#include <usb/xhci.h>
Expand All @@ -23,9 +25,13 @@
/* Declare global data pointer */
DECLARE_GLOBAL_DATA_PTR;

#define NSEC_PER_SEC 1000000000L

struct xhci_imx8m_plat {
struct clk_bulk clks;
struct phy_bulk phys;
struct clk *ref_clk;
bool gfladj_refclk_lpm_sel;
};

static void imx8m_xhci_set_suspend_clk(struct dwc3 *dwc3_reg)
Expand All @@ -40,20 +46,69 @@ static void imx8m_xhci_set_suspend_clk(struct dwc3 *dwc3_reg)
writel(reg, &dwc3_reg->g_ctl);
}

static void imx8m_xhci_sel_24m_refclk(struct dwc3 *dwc3_reg)
static void imx8m_xhci_ref_clk_period(struct dwc3 *dwc3_reg,
struct xhci_imx8m_plat *plat)
{
unsigned long period;
unsigned long fladj;
unsigned long decr;
unsigned long rate;
u32 reg;

/* Set to 24M refclk */
if (plat->ref_clk) {
rate = clk_get_rate(plat->ref_clk);
if (!rate)
return;
period = NSEC_PER_SEC / rate;
} else {
return;
}

reg = readl(&dwc3_reg->g_uctl);
reg &= ~(0xFFC00000);
reg |= (41 << 22);
reg &= ~DWC3_GUCTL_REFCLKPER_MASK;
reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, period);
writel(reg, &dwc3_reg->g_uctl);

writel(0x0a87f000, &dwc3_reg->g_fladj);

/*
* 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 = readl(&dwc3_reg->g_fladj);
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);

if (plat->gfladj_refclk_lpm_sel)
reg |= DWC3_GFLADJ_REFCLK_LPM_SEL;

writel(reg, &dwc3_reg->g_fladj);
}

static int imx8m_xhci_core_init(struct dwc3 *dwc3_reg)
static int imx8m_xhci_core_init(struct dwc3 *dwc3_reg,
struct xhci_imx8m_plat *plat)
{
int ret = 0;

Expand All @@ -68,18 +123,19 @@ static int imx8m_xhci_core_init(struct dwc3 *dwc3_reg)
/* We are hard-coding DWC3 core to Host Mode */
dwc3_set_mode(dwc3_reg, DWC3_GCTL_PRTCAP_HOST);

imx8m_xhci_sel_24m_refclk(dwc3_reg);

/* Set GFLADJ_30MHZ as 20h as per XHCI spec default value */
dwc3_set_fladj(dwc3_reg, GFLADJ_30MHZ_DEFAULT);

/* Adjust Reference Clock Period */
imx8m_xhci_ref_clk_period(dwc3_reg, plat);

return ret;
}

static int xhci_imx8m_clk_init(struct udevice *dev,
struct xhci_imx8m_plat *plat)
{
int ret;
int ret, index;

ret = clk_get_bulk(dev, &plat->clks);
if (ret == -ENOSYS || ret == -ENOENT)
Expand All @@ -93,6 +149,10 @@ static int xhci_imx8m_clk_init(struct udevice *dev,
return ret;
}

index = ofnode_stringlist_search(dev_ofnode(dev), "clock-names", "ref");
if (index >= 0)
plat->ref_clk = &plat->clks.clks[index];

return 0;
}

Expand All @@ -108,6 +168,9 @@ static int xhci_imx8m_probe(struct udevice *dev)
if (ret)
return ret;

plat->gfladj_refclk_lpm_sel = dev_read_bool(dev,
"snps,gfladj-refclk-lpm-sel-quirk");

hccr = (struct xhci_hccr *)((uintptr_t)dev_remap_addr(dev));
if (!hccr)
return -EINVAL;
Expand All @@ -121,7 +184,7 @@ static int xhci_imx8m_probe(struct udevice *dev)

dwc3_reg = (struct dwc3 *)((char *)(hccr) + DWC3_REG_OFFSET);

ret = imx8m_xhci_core_init(dwc3_reg);
ret = imx8m_xhci_core_init(dwc3_reg, plat);
if (ret < 0) {
puts("Failed to initialize imx8m xhci\n");
return ret;
Expand Down
8 changes: 8 additions & 0 deletions include/linux/usb/dwc3.h
Original file line number Diff line number Diff line change
Expand Up @@ -210,11 +210,19 @@ struct dwc3 { /* offset: 0xC100 */
#define DWC3_DCTL_CSFTRST (1 << 30)
#define DWC3_DCTL_LSFTRST (1 << 29)

#define DWC3_GFLADJ_REFCLK_FLADJ_MASK GENMASK(21, 8)
#define DWC3_GFLADJ_REFCLK_LPM_SEL BIT(23)
#define DWC3_GFLADJ_240MHZDECR GENMASK(30, 24)
#define DWC3_GFLADJ_240MHZDECR_PLS1 BIT(31)

/* Global Frame Length Adjustment Register */
#define GFLADJ_30MHZ_REG_SEL (1 << 7)
#define GFLADJ_30MHZ(n) ((n) & 0x3f)
#define GFLADJ_30MHZ_DEFAULT 0x20

/* Global User Control Register*/
#define DWC3_GUCTL_REFCLKPER_MASK 0xffc00000

#ifdef CONFIG_USB_XHCI_DWC3
void dwc3_set_mode(struct dwc3 *dwc3_reg, u32 mode);
void dwc3_core_soft_reset(struct dwc3 *dwc3_reg);
Expand Down

0 comments on commit 4c1cf0a

Please sign in to comment.