4290f51
From patchwork Mon Oct  9 12:00:51 2017
4290f51
Content-Type: text/plain; charset="utf-8"
4290f51
MIME-Version: 1.0
4290f51
Content-Transfer-Encoding: 7bit
4290f51
Subject: [PATCHv4,
4290f51
 2/2] phy: exynos5-usbdrd: Calibrate LOS levels for exynos5420/5800
4290f51
From: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
4290f51
X-Patchwork-Id: 9992809
4290f51
Message-Id: <1507550451-21324-3-git-send-email-andrzej.p@samsung.com>
4290f51
To: linux-samsung-soc@vger.kernel.org, linux-usb@vger.kernel.org,
4290f51
 linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org
4290f51
Cc: Mark Rutland <mark.rutland@arm.com>, Felipe Balbi <balbi@kernel.org>,
4290f51
 Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>,
4290f51
 Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
4290f51
 Russell King <linux@armlinux.org.uk>,
4290f51
 Krzysztof Kozlowski <krzk@kernel.org>, 
4290f51
 Kishon Vijay Abraham I <kishon@ti.com>,
4290f51
 Rob Herring <robh+dt@kernel.org>, Kukjin Kim <kgene@kernel.org>,
4290f51
 Andrzej Pietrasiewicz <andrzej.p@samsung.com>, 
4290f51
 Marek Szyprowski <m.szyprowski@samsung.com>
4290f51
Date: Mon, 09 Oct 2017 14:00:51 +0200
4290f51
4290f51
From: Vivek Gautam <gautam.vivek@samsung.com>
4290f51
4290f51
Adding phy calibration sequence for USB 3.0 DRD PHY present on
4290f51
Exynos5420/5800 systems.
4290f51
This calibration facilitates setting certain PHY parameters viz.
4290f51
the Loss-of-Signal (LOS) Detector Threshold Level, as well as
4290f51
Tx-Vboost-Level for Super-Speed operations.
4290f51
Additionally we also set proper time to wait for RxDetect measurement,
4290f51
for desired PHY reference clock, so as to solve issue with enumeration
4290f51
of few USB 3.0 devices, like Samsung SUM-TSB16S 3.0 USB drive
4290f51
on the controller.
4290f51
4290f51
We are using CR_port for this purpose to send required data
4290f51
to override the LOS values.
4290f51
4290f51
On testing with USB 3.0 devices on USB 3.0 port present on
4290f51
SMDK5420, and peach-pit boards should see following message:
4290f51
usb 2-1: new SuperSpeed USB device number 2 using xhci-hcd
4290f51
4290f51
and without this patch, should see below shown message:
4290f51
usb 1-1: new high-speed USB device number 2 using xhci-hcd
4290f51
4290f51
[Also removed unnecessary extra lines in the register macro definitions]
4290f51
4290f51
Signed-off-by: Vivek Gautam <gautam.vivek@samsung.com>
4290f51
[adapted to use phy_calibrate as entry point]
4290f51
Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
4290f51
---
4290f51
 drivers/phy/samsung/phy-exynos5-usbdrd.c | 183 +++++++++++++++++++++++++++++++
4290f51
 drivers/usb/dwc3/core.c                  |   7 +-
4290f51
 2 files changed, 188 insertions(+), 2 deletions(-)
4290f51
4290f51
diff --git a/drivers/phy/samsung/phy-exynos5-usbdrd.c b/drivers/phy/samsung/phy-exynos5-usbdrd.c
4290f51
index 22c68f5..9e83c15 100644
4290f51
--- a/drivers/phy/samsung/phy-exynos5-usbdrd.c
4290f51
+++ b/drivers/phy/samsung/phy-exynos5-usbdrd.c
4290f51
@@ -90,7 +90,17 @@
4290f51
 #define PHYCLKRST_COMMONONN			BIT(0)
4290f51
 
4290f51
 #define EXYNOS5_DRD_PHYREG0			0x14
4290f51
+#define PHYREG0_SSC_REF_CLK_SEL			BIT(21)
4290f51
+#define PHYREG0_SSC_RANGE			BIT(20)
4290f51
+#define PHYREG0_CR_WRITE			BIT(19)
4290f51
+#define PHYREG0_CR_READ				BIT(18)
4290f51
+#define PHYREG0_CR_DATA_IN(_x)			((_x) << 2)
4290f51
+#define PHYREG0_CR_CAP_DATA			BIT(1)
4290f51
+#define PHYREG0_CR_CAP_ADDR			BIT(0)
4290f51
+
4290f51
 #define EXYNOS5_DRD_PHYREG1			0x18
4290f51
+#define PHYREG1_CR_DATA_OUT(_x)			((_x) << 1)
4290f51
+#define PHYREG1_CR_ACK				BIT(0)
4290f51
 
4290f51
 #define EXYNOS5_DRD_PHYPARAM0			0x1c
4290f51
 
4290f51
@@ -119,6 +129,25 @@
4290f51
 #define EXYNOS5_DRD_PHYRESUME			0x34
4290f51
 #define EXYNOS5_DRD_LINKPORT			0x44
4290f51
 
4290f51
+/* USB 3.0 DRD PHY SS Function Control Reg; accessed by CR_PORT */
4290f51
+#define EXYNOS5_DRD_PHYSS_LOSLEVEL_OVRD_IN		(0x15)
4290f51
+#define LOSLEVEL_OVRD_IN_LOS_BIAS_5420			(0x5 << 13)
4290f51
+#define LOSLEVEL_OVRD_IN_LOS_BIAS_DEFAULT		(0x0 << 13)
4290f51
+#define LOSLEVEL_OVRD_IN_EN				(0x1 << 10)
4290f51
+#define LOSLEVEL_OVRD_IN_LOS_LEVEL_DEFAULT		(0x9 << 0)
4290f51
+
4290f51
+#define EXYNOS5_DRD_PHYSS_TX_VBOOSTLEVEL_OVRD_IN	(0x12)
4290f51
+#define TX_VBOOSTLEVEL_OVRD_IN_VBOOST_5420		(0x5 << 13)
4290f51
+#define TX_VBOOSTLEVEL_OVRD_IN_VBOOST_DEFAULT		(0x4 << 13)
4290f51
+
4290f51
+#define EXYNOS5_DRD_PHYSS_LANE0_TX_DEBUG		(0x1010)
4290f51
+#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_19M2_20M		(0x4 << 4)
4290f51
+#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_24M		(0x8 << 4)
4290f51
+#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_25M_26M		(0x8 << 4)
4290f51
+#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_48M_50M_52M	(0x20 << 4)
4290f51
+#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_62M5		(0x20 << 4)
4290f51
+#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_96M_100M		(0x40 << 4)
4290f51
+
4290f51
 #define KHZ	1000
4290f51
 #define MHZ	(KHZ * KHZ)
4290f51
 
4290f51
@@ -527,6 +556,151 @@ static int exynos5_usbdrd_phy_power_off(struct phy *phy)
4290f51
 	return 0;
4290f51
 }
4290f51
 
4290f51
+static int crport_handshake(struct exynos5_usbdrd_phy *phy_drd,
4290f51
+						u32 val, u32 cmd)
4290f51
+{
4290f51
+	u32 usec = 100;
4290f51
+	unsigned int result;
4290f51
+
4290f51
+	writel(val | cmd, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
4290f51
+
4290f51
+	do {
4290f51
+		result = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1);
4290f51
+		if (result & PHYREG1_CR_ACK)
4290f51
+			break;
4290f51
+
4290f51
+		udelay(1);
4290f51
+	} while (usec-- > 0);
4290f51
+
4290f51
+	if (!usec) {
4290f51
+		dev_err(phy_drd->dev,
4290f51
+			"CRPORT handshake timeout1 (0x%08x)\n", val);
4290f51
+		return -ETIME;
4290f51
+	}
4290f51
+
4290f51
+	usec = 100;
4290f51
+
4290f51
+	writel(val, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
4290f51
+
4290f51
+	do {
4290f51
+		result = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1);
4290f51
+		if (!(result & PHYREG1_CR_ACK))
4290f51
+			break;
4290f51
+
4290f51
+		udelay(1);
4290f51
+	} while (usec-- > 0);
4290f51
+
4290f51
+	if (!usec) {
4290f51
+		dev_err(phy_drd->dev,
4290f51
+			"CRPORT handshake timeout2 (0x%08x)\n", val);
4290f51
+		return -ETIME;
4290f51
+	}
4290f51
+
4290f51
+	return 0;
4290f51
+}
4290f51
+
4290f51
+static int crport_ctrl_write(struct exynos5_usbdrd_phy *phy_drd,
4290f51
+						u32 addr, u32 data)
4290f51
+{
4290f51
+	int ret;
4290f51
+
4290f51
+	/* Write Address */
4290f51
+	writel(PHYREG0_CR_DATA_IN(addr),
4290f51
+		phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
4290f51
+	ret = crport_handshake(phy_drd, PHYREG0_CR_DATA_IN(addr),
4290f51
+				PHYREG0_CR_CAP_ADDR);
4290f51
+	if (ret)
4290f51
+		return ret;
4290f51
+
4290f51
+	/* Write Data */
4290f51
+	writel(PHYREG0_CR_DATA_IN(data),
4290f51
+		phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
4290f51
+	ret = crport_handshake(phy_drd, PHYREG0_CR_DATA_IN(data),
4290f51
+				PHYREG0_CR_CAP_DATA);
4290f51
+	if (ret)
4290f51
+		return ret;
4290f51
+
4290f51
+	ret = crport_handshake(phy_drd, PHYREG0_CR_DATA_IN(data),
4290f51
+				PHYREG0_CR_WRITE);
4290f51
+
4290f51
+	return ret;
4290f51
+}
4290f51
+
4290f51
+/*
4290f51
+ * Calibrate few PHY parameters using CR_PORT register to meet
4290f51
+ * SuperSpeed requirements on Exynos5420 and Exynos5800 systems,
4290f51
+ * which have 28nm USB 3.0 DRD PHY.
4290f51
+ */
4290f51
+static int exynos5420_usbdrd_phy_calibrate(struct exynos5_usbdrd_phy *phy_drd)
4290f51
+{
4290f51
+	unsigned int temp;
4290f51
+	int ret = 0;
4290f51
+
4290f51
+	/*
4290f51
+	 * Change los_bias to (0x5) for 28nm PHY from a
4290f51
+	 * default value (0x0); los_level is set as default
4290f51
+	 * (0x9) as also reflected in los_level[30:26] bits
4290f51
+	 * of PHYPARAM0 register.
4290f51
+	 */
4290f51
+	temp = LOSLEVEL_OVRD_IN_LOS_BIAS_5420 |
4290f51
+		LOSLEVEL_OVRD_IN_EN |
4290f51
+		LOSLEVEL_OVRD_IN_LOS_LEVEL_DEFAULT;
4290f51
+	ret = crport_ctrl_write(phy_drd,
4290f51
+				EXYNOS5_DRD_PHYSS_LOSLEVEL_OVRD_IN,
4290f51
+				temp);
4290f51
+	if (ret) {
4290f51
+		dev_err(phy_drd->dev,
4290f51
+		 "Failed setting Loss-of-Signal level for SuperSpeed\n");
4290f51
+		return ret;
4290f51
+	}
4290f51
+
4290f51
+	/*
4290f51
+	 * Set tx_vboost_lvl to (0x5) for 28nm PHY Tuning,
4290f51
+	 * to raise Tx signal level from its default value of (0x4)
4290f51
+	 */
4290f51
+	temp = TX_VBOOSTLEVEL_OVRD_IN_VBOOST_5420;
4290f51
+	ret = crport_ctrl_write(phy_drd,
4290f51
+				EXYNOS5_DRD_PHYSS_TX_VBOOSTLEVEL_OVRD_IN,
4290f51
+				temp);
4290f51
+	if (ret) {
4290f51
+		dev_err(phy_drd->dev,
4290f51
+		 "Failed setting Tx-Vboost-Level for SuperSpeed\n");
4290f51
+		return ret;
4290f51
+	}
4290f51
+
4290f51
+	/*
4290f51
+	 * Set proper time to wait for RxDetect measurement, for
4290f51
+	 * desired reference clock of PHY, by tuning the CR_PORT
4290f51
+	 * register LANE0.TX_DEBUG which is internal to PHY.
4290f51
+	 * This fixes issue with few USB 3.0 devices, which are
4290f51
+	 * not detected (not even generate interrupts on the bus
4290f51
+	 * on insertion) without this change.
4290f51
+	 * e.g. Samsung SUM-TSB16S 3.0 USB drive.
4290f51
+	 */
4290f51
+	switch (phy_drd->extrefclk) {
4290f51
+	case EXYNOS5_FSEL_50MHZ:
4290f51
+		temp = LANE0_TX_DEBUG_RXDET_MEAS_TIME_48M_50M_52M;
4290f51
+		break;
4290f51
+	case EXYNOS5_FSEL_20MHZ:
4290f51
+	case EXYNOS5_FSEL_19MHZ2:
4290f51
+		temp = LANE0_TX_DEBUG_RXDET_MEAS_TIME_19M2_20M;
4290f51
+		break;
4290f51
+	case EXYNOS5_FSEL_24MHZ:
4290f51
+	default:
4290f51
+		temp = LANE0_TX_DEBUG_RXDET_MEAS_TIME_24M;
4290f51
+		break;
4290f51
+	}
4290f51
+
4290f51
+	ret = crport_ctrl_write(phy_drd,
4290f51
+				EXYNOS5_DRD_PHYSS_LANE0_TX_DEBUG,
4290f51
+				temp);
4290f51
+	if (ret)
4290f51
+		dev_err(phy_drd->dev,
4290f51
+		 "Failed setting RxDetect measurement time for SuperSpeed\n");
4290f51
+
4290f51
+	return ret;
4290f51
+}
4290f51
+
4290f51
 static struct phy *exynos5_usbdrd_phy_xlate(struct device *dev,
4290f51
 					struct of_phandle_args *args)
4290f51
 {
4290f51
@@ -538,11 +712,20 @@ static struct phy *exynos5_usbdrd_phy_xlate(struct device *dev,
4290f51
 	return phy_drd->phys[args->args[0]].phy;
4290f51
 }
4290f51
 
4290f51
+static int exynos5_usbdrd_phy_calibrate(struct phy *phy)
4290f51
+{
4290f51
+	struct phy_usb_instance *inst = phy_get_drvdata(phy);
4290f51
+	struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
4290f51
+
4290f51
+	return exynos5420_usbdrd_phy_calibrate(phy_drd);
4290f51
+}
4290f51
+
4290f51
 static const struct phy_ops exynos5_usbdrd_phy_ops = {
4290f51
 	.init		= exynos5_usbdrd_phy_init,
4290f51
 	.exit		= exynos5_usbdrd_phy_exit,
4290f51
 	.power_on	= exynos5_usbdrd_phy_power_on,
4290f51
 	.power_off	= exynos5_usbdrd_phy_power_off,
4290f51
+	.calibrate	= exynos5_usbdrd_phy_calibrate,
4290f51
 	.owner		= THIS_MODULE,
4290f51
 };
4290f51
 
4290f51
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
4290f51
index 03474d3..224e0dd 100644
4290f51
--- a/drivers/usb/dwc3/core.c
4290f51
+++ b/drivers/usb/dwc3/core.c
4290f51
@@ -156,9 +156,10 @@ static void __dwc3_set_mode(struct work_struct *work)
4290f51
 		} else {
4290f51
 			if (dwc->usb2_phy)
4290f51
 				otg_set_vbus(dwc->usb2_phy->otg, true);
4290f51
-			if (dwc->usb2_generic_phy)
4290f51
+			if (dwc->usb2_generic_phy) {
4290f51
 				phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
4290f51
-
4290f51
+				phy_calibrate(dwc->usb2_generic_phy);
4290f51
+			}
4290f51
 		}
4290f51
 		break;
4290f51
 	case DWC3_GCTL_PRTCAP_DEVICE:
4290f51
@@ -955,6 +956,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
4290f51
 				dev_err(dev, "failed to initialize host\n");
4290f51
 			return ret;
4290f51
 		}
4290f51
+		if (dwc->usb2_generic_phy)
4290f51
+			phy_calibrate(dwc->usb2_generic_phy);
4290f51
 		break;
4290f51
 	case USB_DR_MODE_OTG:
4290f51
 		INIT_WORK(&dwc->drd_work, __dwc3_set_mode);