[PATCH RFC 4/4] phy-exynos-usb3: Fine tune LOS levels for exynos5420

From: Vivek Gautam
Date: Tue Dec 10 2013 - 05:57:08 EST


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-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/