Adding phy tune callback, which facilitates tuning USB 3.0 PHY present on Exynos5420. Basically, Exynos5420 has 28nm PHY for which Loss-of-Signal (LOS) Detector Threshold Level should be controlled for Super-Speed operations. We are using CR_port for this purpose to send required data to override the LOS values. On testing with USB 3.0 devices on USB 3.0 port present on SMDK5420, should see following message: usb 2-1: new SuperSpeed USB device number 2 using xhci-hcd and without this patch, should see below shown message: usb 2-1: new high-speed USB device number 2 using xhci-hcd Signed-off-by: Vivek Gautam <gautam.vivek@xxxxxxxxxxx> --- drivers/phy/phy-exynos5-usb3.c | 107 ++++++++++++++++++++++++++++++++++++++++ 1 files changed, 107 insertions(+), 0 deletions(-) diff --git a/drivers/phy/phy-exynos5-usb3.c b/drivers/phy/phy-exynos5-usb3.c index 2bafc9d..669f998 100644 --- a/drivers/phy/phy-exynos5-usb3.c +++ b/drivers/phy/phy-exynos5-usb3.c @@ -84,8 +84,20 @@ #define PHYCLKRST_COMMONONN (0x1 << 0) #define EXYNOS5_DRD_PHYREG0 (0x14) + +#define EXYNOS5_DRD_PHYREG0_SSC_REF_CLK_SEL (1 << 21) +#define EXYNOS5_DRD_PHYREG0_SSC_RANGE (1 << 20) +#define EXYNOS5_DRD_PHYREG0_CR_WRITE (1 << 19) +#define EXYNOS5_DRD_PHYREG0_CR_READ (1 << 18) +#define EXYNOS5_DRD_PHYREG0_CR_DATA_IN(_x) ((_x) << 2) +#define EXYNOS5_DRD_PHYREG0_CR_CAP_DATA (1 << 1) +#define EXYNOS5_DRD_PHYREG0_CR_CAP_ADDR (1 << 0) + #define EXYNOS5_DRD_PHYREG1 (0x18) +#define EXYNOS5_DRD_PHYREG1_CR_DATA_OUT(_x) ((_x) << 1) +#define EXYNOS5_DRD_PHYREG1_CR_ACK (1 << 0) + #define EXYNOS5_DRD_PHYPARAM0 (0x1c) #define PHYPARAM0_REF_USE_PAD (0x1 << 31) @@ -113,6 +125,18 @@ #define EXYNOS5_DRD_PHYRESUME (0x34) #define EXYNOS5_DRD_LINKPORT (0x44) +/* USB 3.0 DRD PHY SS Function Control Reg; accessed by CR_PORT */ +#define EXYNOS5_DRD_PHYSS_LOSLEVEL_OVRD_IN (0x15) + +#define LOSLEVEL_OVRD_IN_LOS_BIAS_5420 (0x5 << 13) +#define LOSLEVEL_OVRD_IN_LOS_BIAS_DEFAULT (0x0 << 13) +#define LOSLEVEL_OVRD_IN_EN (0x1 << 10) +#define LOSLEVEL_OVRD_IN_LOS_LEVEL_DEFAULT (0x9 << 0) + +#define EXYNOS5_DRD_PHYSS_TX_VBOOSTLEVEL_OVRD_IN (0x12) +#define TX_VBOOSTLEVEL_OVRD_IN_VBOOST_5420 (0x5 << 13) +#define TX_VBOOSTLEVEL_OVRD_IN_VBOOST_DEFAULT (0x4 << 13) + /* Power isolation defined in power management unit */ #define EXYNOS5_USB3DRD_PHY_PMU_REG_OFFSET (0x704) #define EXYNOS5_USB3DRD_PMU_ISOL (1 << 0) @@ -124,6 +148,7 @@ struct usb3phy_config { bool has_usb30_sclk; u32 reg_pmu_offset; bool has_multi_controller; + bool need_crport_tuning; }; struct usb3phy_driver { @@ -235,6 +260,53 @@ static u32 exynos5_usb3phy_set_refclk(struct usb3phy_driver *drv) return reg; } +static void crport_handshake(struct usb3phy_driver *drv, u32 val, u32 cmd) +{ + u32 usec = 100; + u32 result; + + writel(val | cmd, drv->reg_phy + EXYNOS5_DRD_PHYREG0); + + do { + result = readl(drv->reg_phy + EXYNOS5_DRD_PHYREG1); + if (result & EXYNOS5_DRD_PHYREG1_CR_ACK) + break; + + udelay(1); + } while (usec-- > 0); + + if (!usec) + dev_err(drv->dev, "CRPORT handshake timeout1 (0x%08x)\n", val); + + usec = 100; + + writel(val, drv->reg_phy + EXYNOS5_DRD_PHYREG0); + + do { + result = readl(drv->reg_phy + EXYNOS5_DRD_PHYREG1); + if (!(result & EXYNOS5_DRD_PHYREG1_CR_ACK)) + break; + + udelay(1); + } while (usec-- > 0); + + if (!usec) + dev_err(drv->dev, "CRPORT handshake timeout2 (0x%08x)\n", val); +} + +static void crport_ctrl_write(struct usb3phy_driver *drv, u32 addr, u32 data) +{ + /* Write Address */ + crport_handshake(drv, EXYNOS5_DRD_PHYREG0_CR_DATA_IN(addr), + EXYNOS5_DRD_PHYREG0_CR_CAP_ADDR); + + /* Write Data */ + crport_handshake(drv, EXYNOS5_DRD_PHYREG0_CR_DATA_IN(data), + EXYNOS5_DRD_PHYREG0_CR_CAP_DATA); + crport_handshake(drv, EXYNOS5_DRD_PHYREG0_CR_DATA_IN(data), + EXYNOS5_DRD_PHYREG0_CR_WRITE); +} + static int exynos5_usb3phy_init(struct phy *phy) { struct usb3phy_driver *drv = phy_get_drvdata(phy); @@ -379,11 +451,44 @@ static int exynos5_usb3phy_power_off(struct phy *phy) return 0; } +static int exynos5_usb3phy_tune(struct phy *phy) +{ + struct usb3phy_driver *drv = phy_get_drvdata(phy); + + if (drv->cfg->need_crport_tuning) { + u32 temp; + /* + * Change los_bias to (0x5) for 28nm PHY from a + * default value (0x0); los_level is set as default + * (0x9) as also reflected in los_level[30:26] bits + * of PHYPARAM0 register. + */ + temp = LOSLEVEL_OVRD_IN_LOS_BIAS_5420 | + LOSLEVEL_OVRD_IN_EN | + LOSLEVEL_OVRD_IN_LOS_LEVEL_DEFAULT; + crport_ctrl_write(drv, + EXYNOS5_DRD_PHYSS_LOSLEVEL_OVRD_IN, + temp); + + /* + * Set tx_vboost_lvl to (0x5) for 28nm PHY Tuning, + * to raise Tx signal level from its default value of (0x4) + */ + temp = TX_VBOOSTLEVEL_OVRD_IN_VBOOST_5420; + crport_ctrl_write(drv, + EXYNOS5_DRD_PHYSS_TX_VBOOSTLEVEL_OVRD_IN, + temp); + } + + return 0; +} + static struct phy_ops exynos5_usb3phy_ops = { .init = exynos5_usb3phy_init, .exit = exynos5_usb3phy_exit, .power_on = exynos5_usb3phy_power_on, .power_off = exynos5_usb3phy_power_off, + .tune = exynos5_usb3phy_tune, .owner = THIS_MODULE, }; @@ -391,12 +496,14 @@ const struct usb3phy_config exynos5420_usb3phy_cfg = { .has_usb30_sclk = true, .reg_pmu_offset = EXYNOS5_USB3DRD_PHY_PMU_REG_OFFSET, .has_multi_controller = true, + .need_crport_tuning = true, }; const struct usb3phy_config exynos5250_usb3phy_cfg = { .has_usb30_sclk = false, .reg_pmu_offset = EXYNOS5_USB3DRD_PHY_PMU_REG_OFFSET, .has_multi_controller = false, + .need_crport_tuning = false, }; static const struct of_device_id exynos5_usb3phy_of_match[] = { -- 1.7.6.5 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html