ipq806x: add platform usb support
This change adds DWC3 QCOM USB phys and TCSR drivers. These are cherry-picked from the following LKML threads: *dwc3 qcom: https://lkml.org/lkml/2014/9/12/599 *tcsr: https://lkml.org/lkml/2015/2/9/579 We're also adding an additional patch to add the corresponding dev nodes in the IPQ806x and AP148 dts files. Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org> SVN-Revision: 45261
This commit is contained in:
		@@ -0,0 +1,511 @@
 | 
			
		||||
--- a/drivers/phy/Kconfig
 | 
			
		||||
+++ b/drivers/phy/Kconfig
 | 
			
		||||
@@ -256,4 +256,15 @@ config PHY_STIH41X_USB
 | 
			
		||||
 	  Enable this to support the USB transceiver that is part of
 | 
			
		||||
 	  STMicroelectronics STiH41x SoC series.
 | 
			
		||||
 
 | 
			
		||||
+config PHY_QCOM_DWC3
 | 
			
		||||
+	tristate "QCOM DWC3 USB PHY support"
 | 
			
		||||
+	depends on ARCH_QCOM
 | 
			
		||||
+	depends on HAS_IOMEM
 | 
			
		||||
+	depends on OF
 | 
			
		||||
+	select GENERIC_PHY
 | 
			
		||||
+	help
 | 
			
		||||
+	  This option enables support for the Synopsis PHYs present inside the
 | 
			
		||||
+	  Qualcomm USB3.0 DWC3 controller.  This driver supports both HS and SS
 | 
			
		||||
+	  PHY controllers.
 | 
			
		||||
+
 | 
			
		||||
 endmenu
 | 
			
		||||
--- a/drivers/phy/Makefile
 | 
			
		||||
+++ b/drivers/phy/Makefile
 | 
			
		||||
@@ -31,3 +31,4 @@ obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+=
 | 
			
		||||
 obj-$(CONFIG_PHY_XGENE)			+= phy-xgene.o
 | 
			
		||||
 obj-$(CONFIG_PHY_STIH407_USB)		+= phy-stih407-usb.o
 | 
			
		||||
 obj-$(CONFIG_PHY_STIH41X_USB)		+= phy-stih41x-usb.o
 | 
			
		||||
+obj-$(CONFIG_PHY_QCOM_DWC3)		+= phy-qcom-dwc3.o
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/drivers/phy/phy-qcom-dwc3.c
 | 
			
		||||
@@ -0,0 +1,483 @@
 | 
			
		||||
+/* Copyright (c) 2013-2014, Code Aurora Forum. All rights reserved.
 | 
			
		||||
+ *
 | 
			
		||||
+ * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
+ * it under the terms of the GNU General Public License version 2 and
 | 
			
		||||
+ * only version 2 as published by the Free Software Foundation.
 | 
			
		||||
+ *
 | 
			
		||||
+ * This program is distributed in the hope that it will be useful,
 | 
			
		||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
+ * GNU General Public License for more details.
 | 
			
		||||
+ */
 | 
			
		||||
+
 | 
			
		||||
+#include <linux/clk.h>
 | 
			
		||||
+#include <linux/err.h>
 | 
			
		||||
+#include <linux/io.h>
 | 
			
		||||
+#include <linux/module.h>
 | 
			
		||||
+#include <linux/of.h>
 | 
			
		||||
+#include <linux/phy/phy.h>
 | 
			
		||||
+#include <linux/platform_device.h>
 | 
			
		||||
+#include <linux/delay.h>
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ *  USB QSCRATCH Hardware registers
 | 
			
		||||
+ */
 | 
			
		||||
+#define QSCRATCH_GENERAL_CFG		(0x08)
 | 
			
		||||
+#define HSUSB_PHY_CTRL_REG		(0x10)
 | 
			
		||||
+
 | 
			
		||||
+/* PHY_CTRL_REG */
 | 
			
		||||
+#define HSUSB_CTRL_DMSEHV_CLAMP			BIT(24)
 | 
			
		||||
+#define HSUSB_CTRL_USB2_SUSPEND			BIT(23)
 | 
			
		||||
+#define HSUSB_CTRL_UTMI_CLK_EN			BIT(21)
 | 
			
		||||
+#define	HSUSB_CTRL_UTMI_OTG_VBUS_VALID		BIT(20)
 | 
			
		||||
+#define HSUSB_CTRL_USE_CLKCORE			BIT(18)
 | 
			
		||||
+#define HSUSB_CTRL_DPSEHV_CLAMP			BIT(17)
 | 
			
		||||
+#define HSUSB_CTRL_COMMONONN			BIT(11)
 | 
			
		||||
+#define HSUSB_CTRL_ID_HV_CLAMP			BIT(9)
 | 
			
		||||
+#define HSUSB_CTRL_OTGSESSVLD_CLAMP		BIT(8)
 | 
			
		||||
+#define HSUSB_CTRL_CLAMP_EN			BIT(7)
 | 
			
		||||
+#define HSUSB_CTRL_RETENABLEN			BIT(1)
 | 
			
		||||
+#define HSUSB_CTRL_POR				BIT(0)
 | 
			
		||||
+
 | 
			
		||||
+/* QSCRATCH_GENERAL_CFG */
 | 
			
		||||
+#define HSUSB_GCFG_XHCI_REV		BIT(2)
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ *  USB QSCRATCH Hardware registers
 | 
			
		||||
+ */
 | 
			
		||||
+#define SSUSB_PHY_CTRL_REG		(0x00)
 | 
			
		||||
+#define SSUSB_PHY_PARAM_CTRL_1		(0x04)
 | 
			
		||||
+#define SSUSB_PHY_PARAM_CTRL_2		(0x08)
 | 
			
		||||
+#define CR_PROTOCOL_DATA_IN_REG		(0x0c)
 | 
			
		||||
+#define CR_PROTOCOL_DATA_OUT_REG	(0x10)
 | 
			
		||||
+#define CR_PROTOCOL_CAP_ADDR_REG	(0x14)
 | 
			
		||||
+#define CR_PROTOCOL_CAP_DATA_REG	(0x18)
 | 
			
		||||
+#define CR_PROTOCOL_READ_REG		(0x1c)
 | 
			
		||||
+#define CR_PROTOCOL_WRITE_REG		(0x20)
 | 
			
		||||
+
 | 
			
		||||
+/* PHY_CTRL_REG */
 | 
			
		||||
+#define SSUSB_CTRL_REF_USE_PAD		BIT(28)
 | 
			
		||||
+#define SSUSB_CTRL_TEST_POWERDOWN	BIT(27)
 | 
			
		||||
+#define SSUSB_CTRL_LANE0_PWR_PRESENT	BIT(24)
 | 
			
		||||
+#define SSUSB_CTRL_SS_PHY_EN		BIT(8)
 | 
			
		||||
+#define SSUSB_CTRL_SS_PHY_RESET		BIT(7)
 | 
			
		||||
+
 | 
			
		||||
+/* SSPHY control registers */
 | 
			
		||||
+#define SSPHY_CTRL_RX_OVRD_IN_HI(lane)	(0x1006 + 0x100 * lane)
 | 
			
		||||
+#define SSPHY_CTRL_TX_OVRD_DRV_LO(lane)	(0x1002 + 0x100 * lane)
 | 
			
		||||
+
 | 
			
		||||
+/* RX OVRD IN HI bits */
 | 
			
		||||
+#define RX_OVRD_IN_HI_RX_RESET_OVRD		BIT(13)
 | 
			
		||||
+#define RX_OVRD_IN_HI_RX_RX_RESET		BIT(12)
 | 
			
		||||
+#define RX_OVRD_IN_HI_RX_EQ_OVRD		BIT(11)
 | 
			
		||||
+#define RX_OVRD_IN_HI_RX_EQ_MASK		0x0700
 | 
			
		||||
+#define RX_OVRD_IN_HI_RX_EQ_SHIFT		8
 | 
			
		||||
+#define RX_OVRD_IN_HI_RX_EQ_EN_OVRD		BIT(7)
 | 
			
		||||
+#define RX_OVRD_IN_HI_RX_EQ_EN			BIT(6)
 | 
			
		||||
+#define RX_OVRD_IN_HI_RX_LOS_FILTER_OVRD	BIT(5)
 | 
			
		||||
+#define RX_OVRD_IN_HI_RX_LOS_FILTER_MASK	0x0018
 | 
			
		||||
+#define RX_OVRD_IN_HI_RX_RATE_OVRD		BIT(2)
 | 
			
		||||
+#define RX_OVRD_IN_HI_RX_RATE_MASK		0x0003
 | 
			
		||||
+
 | 
			
		||||
+/* TX OVRD DRV LO register bits */
 | 
			
		||||
+#define TX_OVRD_DRV_LO_AMPLITUDE_MASK	0x007F
 | 
			
		||||
+#define TX_OVRD_DRV_LO_PREEMPH_MASK	0x3F80
 | 
			
		||||
+#define TX_OVRD_DRV_LO_PREEMPH_SHIFT	7
 | 
			
		||||
+#define TX_OVRD_DRV_LO_EN		BIT(14)
 | 
			
		||||
+
 | 
			
		||||
+struct qcom_dwc3_usb_phy {
 | 
			
		||||
+	void __iomem		*base;
 | 
			
		||||
+	struct device		*dev;
 | 
			
		||||
+	struct phy *phy;
 | 
			
		||||
+
 | 
			
		||||
+	int (*phy_init)(struct qcom_dwc3_usb_phy *phy_dwc3);
 | 
			
		||||
+	int (*phy_exit)(struct qcom_dwc3_usb_phy *phy_dwc3);
 | 
			
		||||
+
 | 
			
		||||
+	struct clk		*xo_clk;
 | 
			
		||||
+	struct clk		*ref_clk;
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * Write register and read back masked value to confirm it is written
 | 
			
		||||
+ *
 | 
			
		||||
+ * @base - QCOM DWC3 PHY base virtual address.
 | 
			
		||||
+ * @offset - register offset.
 | 
			
		||||
+ * @mask - register bitmask specifying what should be updated
 | 
			
		||||
+ * @val - value to write.
 | 
			
		||||
+ */
 | 
			
		||||
+static inline void qcom_dwc3_phy_write_readback(
 | 
			
		||||
+	struct qcom_dwc3_usb_phy *phy_dwc3, u32 offset,
 | 
			
		||||
+	const u32 mask, u32 val)
 | 
			
		||||
+{
 | 
			
		||||
+	u32 write_val, tmp = readl(phy_dwc3->base + offset);
 | 
			
		||||
+
 | 
			
		||||
+	tmp &= ~mask;		/* retain other bits */
 | 
			
		||||
+	write_val = tmp | val;
 | 
			
		||||
+
 | 
			
		||||
+	writel(write_val, phy_dwc3->base + offset);
 | 
			
		||||
+
 | 
			
		||||
+	/* Read back to see if val was written */
 | 
			
		||||
+	tmp = readl(phy_dwc3->base + offset);
 | 
			
		||||
+	tmp &= mask;		/* clear other bits */
 | 
			
		||||
+
 | 
			
		||||
+	if (tmp != val)
 | 
			
		||||
+		dev_err(phy_dwc3->dev, "write: %x to QSCRATCH: %x FAILED\n",
 | 
			
		||||
+			val, offset);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static int wait_for_latch(void __iomem *addr)
 | 
			
		||||
+{
 | 
			
		||||
+	u32 retry = 10;
 | 
			
		||||
+
 | 
			
		||||
+	while (true) {
 | 
			
		||||
+		if (!readl(addr))
 | 
			
		||||
+			break;
 | 
			
		||||
+
 | 
			
		||||
+		if (--retry == 0)
 | 
			
		||||
+			return -ETIMEDOUT;
 | 
			
		||||
+
 | 
			
		||||
+		usleep_range(10, 20);
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	return 0;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * Write SSPHY register
 | 
			
		||||
+ *
 | 
			
		||||
+ * @base - QCOM DWC3 PHY base virtual address.
 | 
			
		||||
+ * @addr - SSPHY address to write.
 | 
			
		||||
+ * @val - value to write.
 | 
			
		||||
+ */
 | 
			
		||||
+static int qcom_dwc3_ss_write_phycreg(void __iomem *base, u32 addr, u32 val)
 | 
			
		||||
+{
 | 
			
		||||
+	int ret;
 | 
			
		||||
+
 | 
			
		||||
+	writel(addr, base + CR_PROTOCOL_DATA_IN_REG);
 | 
			
		||||
+	writel(0x1, base + CR_PROTOCOL_CAP_ADDR_REG);
 | 
			
		||||
+
 | 
			
		||||
+	ret = wait_for_latch(base + CR_PROTOCOL_CAP_ADDR_REG);
 | 
			
		||||
+	if (ret)
 | 
			
		||||
+		goto err_wait;
 | 
			
		||||
+
 | 
			
		||||
+	writel(val, base + CR_PROTOCOL_DATA_IN_REG);
 | 
			
		||||
+	writel(0x1, base + CR_PROTOCOL_CAP_DATA_REG);
 | 
			
		||||
+
 | 
			
		||||
+	ret = wait_for_latch(base + CR_PROTOCOL_CAP_DATA_REG);
 | 
			
		||||
+	if (ret)
 | 
			
		||||
+		goto err_wait;
 | 
			
		||||
+
 | 
			
		||||
+	writel(0x1, base + CR_PROTOCOL_WRITE_REG);
 | 
			
		||||
+
 | 
			
		||||
+	ret = wait_for_latch(base + CR_PROTOCOL_WRITE_REG);
 | 
			
		||||
+
 | 
			
		||||
+err_wait:
 | 
			
		||||
+	return ret;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * Read SSPHY register.
 | 
			
		||||
+ *
 | 
			
		||||
+ * @base - QCOM DWC3 PHY base virtual address.
 | 
			
		||||
+ * @addr - SSPHY address to read.
 | 
			
		||||
+ */
 | 
			
		||||
+static int qcom_dwc3_ss_read_phycreg(void __iomem *base, u32 addr, u32 *val)
 | 
			
		||||
+{
 | 
			
		||||
+	int ret;
 | 
			
		||||
+	bool first_read = true;
 | 
			
		||||
+
 | 
			
		||||
+	writel(addr, base + CR_PROTOCOL_DATA_IN_REG);
 | 
			
		||||
+	writel(0x1, base + CR_PROTOCOL_CAP_ADDR_REG);
 | 
			
		||||
+
 | 
			
		||||
+	ret = wait_for_latch(base + CR_PROTOCOL_CAP_ADDR_REG);
 | 
			
		||||
+	if (ret)
 | 
			
		||||
+		goto err_wait;
 | 
			
		||||
+
 | 
			
		||||
+	/*
 | 
			
		||||
+	 * Due to hardware bug, first read of SSPHY register might be
 | 
			
		||||
+	 * incorrect. Hence as workaround, SW should perform SSPHY register
 | 
			
		||||
+	 * read twice, but use only second read and ignore first read.
 | 
			
		||||
+	 */
 | 
			
		||||
+retry:
 | 
			
		||||
+	writel(0x1, base + CR_PROTOCOL_READ_REG);
 | 
			
		||||
+
 | 
			
		||||
+	ret = wait_for_latch(base + CR_PROTOCOL_READ_REG);
 | 
			
		||||
+	if (ret)
 | 
			
		||||
+		goto err_wait;
 | 
			
		||||
+
 | 
			
		||||
+	if (first_read) {
 | 
			
		||||
+		readl(base + CR_PROTOCOL_DATA_OUT_REG);
 | 
			
		||||
+		first_read = false;
 | 
			
		||||
+		goto retry;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	*val = readl(base + CR_PROTOCOL_DATA_OUT_REG);
 | 
			
		||||
+
 | 
			
		||||
+err_wait:
 | 
			
		||||
+	return ret;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static int qcom_dwc3_phy_power_on(struct phy *phy)
 | 
			
		||||
+{
 | 
			
		||||
+	int ret;
 | 
			
		||||
+	struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
 | 
			
		||||
+
 | 
			
		||||
+	ret = clk_prepare_enable(phy_dwc3->xo_clk);
 | 
			
		||||
+	if (ret)
 | 
			
		||||
+		return ret;
 | 
			
		||||
+
 | 
			
		||||
+	ret = clk_prepare_enable(phy_dwc3->ref_clk);
 | 
			
		||||
+	if (ret)
 | 
			
		||||
+		clk_disable_unprepare(phy_dwc3->xo_clk);
 | 
			
		||||
+
 | 
			
		||||
+	return ret;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static int qcom_dwc3_phy_power_off(struct phy *phy)
 | 
			
		||||
+{
 | 
			
		||||
+	struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
 | 
			
		||||
+
 | 
			
		||||
+	clk_disable_unprepare(phy_dwc3->ref_clk);
 | 
			
		||||
+	clk_disable_unprepare(phy_dwc3->xo_clk);
 | 
			
		||||
+
 | 
			
		||||
+	return 0;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static int qcom_dwc3_hs_phy_init(struct qcom_dwc3_usb_phy *phy_dwc3)
 | 
			
		||||
+{
 | 
			
		||||
+	u32 val;
 | 
			
		||||
+
 | 
			
		||||
+	/*
 | 
			
		||||
+	 * HSPHY Initialization: Enable UTMI clock, select 19.2MHz fsel
 | 
			
		||||
+	 * enable clamping, and disable RETENTION (power-on default is ENABLED)
 | 
			
		||||
+	 */
 | 
			
		||||
+	val = HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_DMSEHV_CLAMP |
 | 
			
		||||
+		HSUSB_CTRL_RETENABLEN  | HSUSB_CTRL_COMMONONN |
 | 
			
		||||
+		HSUSB_CTRL_OTGSESSVLD_CLAMP | HSUSB_CTRL_ID_HV_CLAMP |
 | 
			
		||||
+		HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_UTMI_OTG_VBUS_VALID |
 | 
			
		||||
+		HSUSB_CTRL_UTMI_CLK_EN | HSUSB_CTRL_CLAMP_EN | 0x70;
 | 
			
		||||
+
 | 
			
		||||
+	/* use core clock if external reference is not present */
 | 
			
		||||
+	if (!phy_dwc3->xo_clk)
 | 
			
		||||
+		val |= HSUSB_CTRL_USE_CLKCORE;
 | 
			
		||||
+
 | 
			
		||||
+	writel(val, phy_dwc3->base + HSUSB_PHY_CTRL_REG);
 | 
			
		||||
+	usleep_range(2000, 2200);
 | 
			
		||||
+
 | 
			
		||||
+	/* Disable (bypass) VBUS and ID filters */
 | 
			
		||||
+	writel(HSUSB_GCFG_XHCI_REV, phy_dwc3->base + QSCRATCH_GENERAL_CFG);
 | 
			
		||||
+
 | 
			
		||||
+	return 0;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static int qcom_dwc3_ss_phy_init(struct qcom_dwc3_usb_phy *phy_dwc3)
 | 
			
		||||
+{
 | 
			
		||||
+	int ret;
 | 
			
		||||
+	u32 data = 0;
 | 
			
		||||
+
 | 
			
		||||
+	/* reset phy */
 | 
			
		||||
+	data = readl_relaxed(phy_dwc3->base + SSUSB_PHY_CTRL_REG);
 | 
			
		||||
+	writel_relaxed(data | SSUSB_CTRL_SS_PHY_RESET,
 | 
			
		||||
+		phy_dwc3->base + SSUSB_PHY_CTRL_REG);
 | 
			
		||||
+	usleep_range(2000, 2200);
 | 
			
		||||
+	writel_relaxed(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
 | 
			
		||||
+
 | 
			
		||||
+	/* clear REF_PAD if we don't have XO clk */
 | 
			
		||||
+	if (!phy_dwc3->xo_clk)
 | 
			
		||||
+		data &= ~SSUSB_CTRL_REF_USE_PAD;
 | 
			
		||||
+	else
 | 
			
		||||
+		data |= SSUSB_CTRL_REF_USE_PAD;
 | 
			
		||||
+
 | 
			
		||||
+	writel_relaxed(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
 | 
			
		||||
+	msleep(30);
 | 
			
		||||
+
 | 
			
		||||
+	data |= SSUSB_CTRL_SS_PHY_EN | SSUSB_CTRL_LANE0_PWR_PRESENT;
 | 
			
		||||
+	writel_relaxed(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
 | 
			
		||||
+
 | 
			
		||||
+	/*
 | 
			
		||||
+	 * Fix RX Equalization setting as follows
 | 
			
		||||
+	 * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0
 | 
			
		||||
+	 * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1
 | 
			
		||||
+	 * LANE0.RX_OVRD_IN_HI.RX_EQ set to 3
 | 
			
		||||
+	 * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1
 | 
			
		||||
+	 */
 | 
			
		||||
+	ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base,
 | 
			
		||||
+			SSPHY_CTRL_RX_OVRD_IN_HI(0), &data);
 | 
			
		||||
+	if (ret)
 | 
			
		||||
+		goto err_phy_trans;
 | 
			
		||||
+
 | 
			
		||||
+	data &= ~RX_OVRD_IN_HI_RX_EQ_EN;
 | 
			
		||||
+	data |= RX_OVRD_IN_HI_RX_EQ_EN_OVRD;
 | 
			
		||||
+	data &= ~RX_OVRD_IN_HI_RX_EQ_MASK;
 | 
			
		||||
+	data |= 0x3 << RX_OVRD_IN_HI_RX_EQ_SHIFT;
 | 
			
		||||
+	data |= RX_OVRD_IN_HI_RX_EQ_OVRD;
 | 
			
		||||
+	ret = qcom_dwc3_ss_write_phycreg(phy_dwc3->base,
 | 
			
		||||
+		SSPHY_CTRL_RX_OVRD_IN_HI(0), data);
 | 
			
		||||
+	if (ret)
 | 
			
		||||
+		goto err_phy_trans;
 | 
			
		||||
+
 | 
			
		||||
+	/*
 | 
			
		||||
+	 * Set EQ and TX launch amplitudes as follows
 | 
			
		||||
+	 * LANE0.TX_OVRD_DRV_LO.PREEMPH set to 22
 | 
			
		||||
+	 * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127
 | 
			
		||||
+	 * LANE0.TX_OVRD_DRV_LO.EN set to 1.
 | 
			
		||||
+	 */
 | 
			
		||||
+	ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base,
 | 
			
		||||
+		SSPHY_CTRL_TX_OVRD_DRV_LO(0), &data);
 | 
			
		||||
+	if (ret)
 | 
			
		||||
+		goto err_phy_trans;
 | 
			
		||||
+
 | 
			
		||||
+	data &= ~TX_OVRD_DRV_LO_PREEMPH_MASK;
 | 
			
		||||
+	data |= 0x16 << TX_OVRD_DRV_LO_PREEMPH_SHIFT;
 | 
			
		||||
+	data &= ~TX_OVRD_DRV_LO_AMPLITUDE_MASK;
 | 
			
		||||
+	data |= 0x7f;
 | 
			
		||||
+	data |= TX_OVRD_DRV_LO_EN;
 | 
			
		||||
+	ret = qcom_dwc3_ss_write_phycreg(phy_dwc3->base,
 | 
			
		||||
+		SSPHY_CTRL_TX_OVRD_DRV_LO(0), data);
 | 
			
		||||
+	if (ret)
 | 
			
		||||
+		goto err_phy_trans;
 | 
			
		||||
+
 | 
			
		||||
+	/*
 | 
			
		||||
+	 * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows
 | 
			
		||||
+	 * TX_FULL_SWING [26:20] amplitude to 127
 | 
			
		||||
+	 * TX_DEEMPH_3_5DB [13:8] to 22
 | 
			
		||||
+	 * LOS_BIAS [2:0] to 0x5
 | 
			
		||||
+	 */
 | 
			
		||||
+	qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_PARAM_CTRL_1,
 | 
			
		||||
+				   0x07f03f07, 0x07f01605);
 | 
			
		||||
+
 | 
			
		||||
+err_phy_trans:
 | 
			
		||||
+	return ret;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static int qcom_dwc3_ss_phy_exit(struct qcom_dwc3_usb_phy *phy_dwc3)
 | 
			
		||||
+{
 | 
			
		||||
+	/* Sequence to put SSPHY in low power state:
 | 
			
		||||
+	 * 1. Clear REF_PHY_EN in PHY_CTRL_REG
 | 
			
		||||
+	 * 2. Clear REF_USE_PAD in PHY_CTRL_REG
 | 
			
		||||
+	 * 3. Set TEST_POWERED_DOWN in PHY_CTRL_REG to enable PHY retention
 | 
			
		||||
+	 * 4. Disable SSPHY ref clk
 | 
			
		||||
+	 */
 | 
			
		||||
+	qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
 | 
			
		||||
+		SSUSB_CTRL_SS_PHY_EN, 0x0);
 | 
			
		||||
+	qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
 | 
			
		||||
+		SSUSB_CTRL_REF_USE_PAD, 0x0);
 | 
			
		||||
+	qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
 | 
			
		||||
+		0x0, SSUSB_CTRL_TEST_POWERDOWN);
 | 
			
		||||
+
 | 
			
		||||
+	return 0;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static int qcom_dwc3_phy_init(struct phy *phy)
 | 
			
		||||
+{
 | 
			
		||||
+	struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
 | 
			
		||||
+
 | 
			
		||||
+	if (phy_dwc3->phy_init)
 | 
			
		||||
+		return phy_dwc3->phy_init(phy_dwc3);
 | 
			
		||||
+
 | 
			
		||||
+	return 0;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static int qcom_dwc3_phy_exit(struct phy *phy)
 | 
			
		||||
+{
 | 
			
		||||
+	struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
 | 
			
		||||
+
 | 
			
		||||
+	if (phy_dwc3->phy_exit)
 | 
			
		||||
+		return qcom_dwc3_ss_phy_exit(phy_dwc3);
 | 
			
		||||
+
 | 
			
		||||
+	return 0;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static struct phy_ops qcom_dwc3_phy_ops = {
 | 
			
		||||
+	.init		= qcom_dwc3_phy_init,
 | 
			
		||||
+	.exit		= qcom_dwc3_phy_exit,
 | 
			
		||||
+	.power_on	= qcom_dwc3_phy_power_on,
 | 
			
		||||
+	.power_off	= qcom_dwc3_phy_power_off,
 | 
			
		||||
+	.owner		= THIS_MODULE,
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+static const struct of_device_id qcom_dwc3_phy_table[] = {
 | 
			
		||||
+	{ .compatible = "qcom,dwc3-hs-usb-phy", },
 | 
			
		||||
+	{ .compatible = "qcom,dwc3-ss-usb-phy", },
 | 
			
		||||
+	{ /* Sentinel */ }
 | 
			
		||||
+};
 | 
			
		||||
+MODULE_DEVICE_TABLE(of, qcom_dwc3_phy_table);
 | 
			
		||||
+
 | 
			
		||||
+static int qcom_dwc3_phy_probe(struct platform_device *pdev)
 | 
			
		||||
+{
 | 
			
		||||
+	struct qcom_dwc3_usb_phy	*phy_dwc3;
 | 
			
		||||
+	struct phy_provider		*phy_provider;
 | 
			
		||||
+	struct resource			*res;
 | 
			
		||||
+
 | 
			
		||||
+	phy_dwc3 = devm_kzalloc(&pdev->dev, sizeof(*phy_dwc3), GFP_KERNEL);
 | 
			
		||||
+	if (!phy_dwc3)
 | 
			
		||||
+		return -ENOMEM;
 | 
			
		||||
+
 | 
			
		||||
+	platform_set_drvdata(pdev, phy_dwc3);
 | 
			
		||||
+
 | 
			
		||||
+	phy_dwc3->dev = &pdev->dev;
 | 
			
		||||
+
 | 
			
		||||
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 | 
			
		||||
+	phy_dwc3->base = devm_ioremap_resource(phy_dwc3->dev, res);
 | 
			
		||||
+	if (IS_ERR(phy_dwc3->base))
 | 
			
		||||
+		return PTR_ERR(phy_dwc3->base);
 | 
			
		||||
+
 | 
			
		||||
+	phy_dwc3->ref_clk = devm_clk_get(phy_dwc3->dev, "ref");
 | 
			
		||||
+	if (IS_ERR(phy_dwc3->ref_clk)) {
 | 
			
		||||
+		dev_dbg(phy_dwc3->dev, "cannot get reference clock\n");
 | 
			
		||||
+		return PTR_ERR(phy_dwc3->ref_clk);
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	if (of_device_is_compatible(pdev->dev.of_node,
 | 
			
		||||
+			"qcom,dwc3-hs-usb-phy")) {
 | 
			
		||||
+		clk_set_rate(phy_dwc3->ref_clk, 60000000);
 | 
			
		||||
+		phy_dwc3->phy_init = qcom_dwc3_hs_phy_init;
 | 
			
		||||
+	} else if (of_device_is_compatible(pdev->dev.of_node,
 | 
			
		||||
+			"qcom,dwc3-ss-usb-phy")) {
 | 
			
		||||
+		phy_dwc3->phy_init = qcom_dwc3_ss_phy_init;
 | 
			
		||||
+		phy_dwc3->phy_exit = qcom_dwc3_ss_phy_exit;
 | 
			
		||||
+		clk_set_rate(phy_dwc3->ref_clk, 125000000);
 | 
			
		||||
+	} else {
 | 
			
		||||
+		dev_err(phy_dwc3->dev, "Unknown phy\n");
 | 
			
		||||
+		return -EINVAL;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	phy_dwc3->xo_clk = devm_clk_get(phy_dwc3->dev, "xo");
 | 
			
		||||
+	if (IS_ERR(phy_dwc3->xo_clk)) {
 | 
			
		||||
+		dev_dbg(phy_dwc3->dev, "cannot get TCXO clock\n");
 | 
			
		||||
+		phy_dwc3->xo_clk = NULL;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	phy_dwc3->phy = devm_phy_create(phy_dwc3->dev, NULL, &qcom_dwc3_phy_ops,
 | 
			
		||||
+					NULL);
 | 
			
		||||
+
 | 
			
		||||
+	if (IS_ERR(phy_dwc3->phy))
 | 
			
		||||
+		return PTR_ERR(phy_dwc3->phy);
 | 
			
		||||
+
 | 
			
		||||
+	phy_set_drvdata(phy_dwc3->phy, phy_dwc3);
 | 
			
		||||
+
 | 
			
		||||
+	phy_provider = devm_of_phy_provider_register(phy_dwc3->dev,
 | 
			
		||||
+			of_phy_simple_xlate);
 | 
			
		||||
+
 | 
			
		||||
+	if (IS_ERR(phy_provider))
 | 
			
		||||
+		return PTR_ERR(phy_provider);
 | 
			
		||||
+
 | 
			
		||||
+	return 0;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static struct platform_driver qcom_dwc3_phy_driver = {
 | 
			
		||||
+	.probe		= qcom_dwc3_phy_probe,
 | 
			
		||||
+	.driver		= {
 | 
			
		||||
+		.name	= "qcom-dwc3-usb-phy",
 | 
			
		||||
+		.owner	= THIS_MODULE,
 | 
			
		||||
+		.of_match_table = qcom_dwc3_phy_table,
 | 
			
		||||
+	},
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+module_platform_driver(qcom_dwc3_phy_driver);
 | 
			
		||||
+
 | 
			
		||||
+MODULE_ALIAS("platform:phy-qcom-dwc3");
 | 
			
		||||
+MODULE_LICENSE("GPL v2");
 | 
			
		||||
+MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");
 | 
			
		||||
+MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
 | 
			
		||||
+MODULE_DESCRIPTION("DesignWare USB3 QCOM PHY driver");
 | 
			
		||||
@@ -0,0 +1,125 @@
 | 
			
		||||
--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
 | 
			
		||||
+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
 | 
			
		||||
@@ -112,5 +112,29 @@
 | 
			
		||||
 		sata@29000000 {
 | 
			
		||||
 			status = "ok";
 | 
			
		||||
 		};
 | 
			
		||||
+
 | 
			
		||||
+		phy@100f8800 {		/* USB3 port 1 HS phy */
 | 
			
		||||
+			status = "ok";
 | 
			
		||||
+		};
 | 
			
		||||
+
 | 
			
		||||
+		phy@100f8830 {		/* USB3 port 1 SS phy */
 | 
			
		||||
+			status = "ok";
 | 
			
		||||
+		};
 | 
			
		||||
+
 | 
			
		||||
+		phy@110f8800 {		/* USB3 port 0 HS phy */
 | 
			
		||||
+			status = "ok";
 | 
			
		||||
+		};
 | 
			
		||||
+
 | 
			
		||||
+		phy@110f8830 {		/* USB3 port 0 SS phy */
 | 
			
		||||
+			status = "ok";
 | 
			
		||||
+		};
 | 
			
		||||
+
 | 
			
		||||
+		usb30@0 {
 | 
			
		||||
+			status = "ok";
 | 
			
		||||
+		};
 | 
			
		||||
+
 | 
			
		||||
+		usb30@1 {
 | 
			
		||||
+			status = "ok";
 | 
			
		||||
+		};
 | 
			
		||||
 	};
 | 
			
		||||
 };
 | 
			
		||||
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
 | 
			
		||||
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
 | 
			
		||||
@@ -291,5 +291,90 @@
 | 
			
		||||
 			#clock-cells = <1>;
 | 
			
		||||
 			#reset-cells = <1>;
 | 
			
		||||
 		};
 | 
			
		||||
+
 | 
			
		||||
+		hs_phy_1: phy@100f8800 {
 | 
			
		||||
+			compatible = "qcom,dwc3-hs-usb-phy";
 | 
			
		||||
+			reg = <0x100f8800 0x30>;
 | 
			
		||||
+			clocks = <&gcc USB30_1_UTMI_CLK>;
 | 
			
		||||
+			clock-names = "ref";
 | 
			
		||||
+			#phy-cells = <0>;
 | 
			
		||||
+
 | 
			
		||||
+			status = "disabled";
 | 
			
		||||
+		};
 | 
			
		||||
+
 | 
			
		||||
+		ss_phy_1: phy@100f8830 {
 | 
			
		||||
+			compatible = "qcom,dwc3-ss-usb-phy";
 | 
			
		||||
+			reg = <0x100f8830 0x30>;
 | 
			
		||||
+			clocks = <&gcc USB30_1_MASTER_CLK>;
 | 
			
		||||
+			clock-names = "ref";
 | 
			
		||||
+			#phy-cells = <0>;
 | 
			
		||||
+
 | 
			
		||||
+			status = "disabled";
 | 
			
		||||
+		};
 | 
			
		||||
+
 | 
			
		||||
+		hs_phy_0: phy@110f8800 {
 | 
			
		||||
+			compatible = "qcom,dwc3-hs-usb-phy";
 | 
			
		||||
+			reg = <0x110f8800 0x30>;
 | 
			
		||||
+			clocks = <&gcc USB30_0_UTMI_CLK>;
 | 
			
		||||
+			clock-names = "ref";
 | 
			
		||||
+			#phy-cells = <0>;
 | 
			
		||||
+
 | 
			
		||||
+			status = "disabled";
 | 
			
		||||
+		};
 | 
			
		||||
+
 | 
			
		||||
+		ss_phy_0: phy@110f8830 {
 | 
			
		||||
+			compatible = "qcom,dwc3-ss-usb-phy";
 | 
			
		||||
+			reg = <0x110f8830 0x30>;
 | 
			
		||||
+			clocks = <&gcc USB30_0_MASTER_CLK>;
 | 
			
		||||
+			clock-names = "ref";
 | 
			
		||||
+			#phy-cells = <0>;
 | 
			
		||||
+
 | 
			
		||||
+			status = "disabled";
 | 
			
		||||
+		};
 | 
			
		||||
+
 | 
			
		||||
+		usb3_0: usb30@0 {
 | 
			
		||||
+			compatible = "qcom,dwc3";
 | 
			
		||||
+			#address-cells = <1>;
 | 
			
		||||
+			#size-cells = <1>;
 | 
			
		||||
+			clocks = <&gcc USB30_0_MASTER_CLK>;
 | 
			
		||||
+			clock-names = "core";
 | 
			
		||||
+
 | 
			
		||||
+			ranges;
 | 
			
		||||
+
 | 
			
		||||
+			status = "disabled";
 | 
			
		||||
+
 | 
			
		||||
+			dwc3@11000000 {
 | 
			
		||||
+				compatible = "snps,dwc3";
 | 
			
		||||
+				reg = <0x11000000 0xcd00>;
 | 
			
		||||
+				interrupts = <0 110 0x4>;
 | 
			
		||||
+				phys = <&hs_phy_0>, <&ss_phy_0>;
 | 
			
		||||
+				phy-names = "usb2-phy", "usb3-phy";
 | 
			
		||||
+				tx-fifo-resize;
 | 
			
		||||
+				dr_mode = "host";
 | 
			
		||||
+			};
 | 
			
		||||
+		};
 | 
			
		||||
+
 | 
			
		||||
+		usb3_1: usb30@1 {
 | 
			
		||||
+			compatible = "qcom,dwc3";
 | 
			
		||||
+			#address-cells = <1>;
 | 
			
		||||
+			#size-cells = <1>;
 | 
			
		||||
+			clocks = <&gcc USB30_1_MASTER_CLK>;
 | 
			
		||||
+			clock-names = "core";
 | 
			
		||||
+
 | 
			
		||||
+			ranges;
 | 
			
		||||
+
 | 
			
		||||
+			status = "disabled";
 | 
			
		||||
+
 | 
			
		||||
+			dwc3@10000000 {
 | 
			
		||||
+				compatible = "snps,dwc3";
 | 
			
		||||
+				reg = <0x10000000 0xcd00>;
 | 
			
		||||
+				interrupts = <0 205 0x4>;
 | 
			
		||||
+				phys = <&hs_phy_1>, <&ss_phy_1>;
 | 
			
		||||
+				phy-names = "usb2-phy", "usb3-phy";
 | 
			
		||||
+				tx-fifo-resize;
 | 
			
		||||
+				dr_mode = "host";
 | 
			
		||||
+			};
 | 
			
		||||
+		};
 | 
			
		||||
+
 | 
			
		||||
 	};
 | 
			
		||||
 };
 | 
			
		||||
@@ -0,0 +1,258 @@
 | 
			
		||||
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt
 | 
			
		||||
index 4ce24d4..186e5f4 100644
 | 
			
		||||
--- a/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt
 | 
			
		||||
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt
 | 
			
		||||
@@ -6,7 +6,8 @@ configuration settings.  The mode setting will govern the input/output mode of
 | 
			
		||||
 the 4 GSBI IOs.
 | 
			
		||||
 
 | 
			
		||||
 Required properties:
 | 
			
		||||
-- compatible: must contain "qcom,gsbi-v1.0.0" for APQ8064/IPQ8064
 | 
			
		||||
+- compatible:	Should contain "qcom,gsbi-v1.0.0"
 | 
			
		||||
+- cell-index:	Should contain the GSBI index
 | 
			
		||||
 - reg: Address range for GSBI registers
 | 
			
		||||
 - clocks: required clock
 | 
			
		||||
 - clock-names: must contain "iface" entry
 | 
			
		||||
@@ -16,6 +17,8 @@ Required properties:
 | 
			
		||||
 Optional properties:
 | 
			
		||||
 - qcom,crci : indicates CRCI MUX value for QUP CRCI ports.  Please reference
 | 
			
		||||
   dt-bindings/soc/qcom,gsbi.h for valid CRCI mux values.
 | 
			
		||||
+- syscon-tcsr: indicates phandle of TCSR syscon node.  Required if child uses
 | 
			
		||||
+  dma.
 | 
			
		||||
 
 | 
			
		||||
 Required properties if child node exists:
 | 
			
		||||
 - #address-cells: Must be 1
 | 
			
		||||
@@ -39,6 +42,7 @@ Example for APQ8064:
 | 
			
		||||
 
 | 
			
		||||
 	gsbi4@16300000 {
 | 
			
		||||
 		compatible = "qcom,gsbi-v1.0.0";
 | 
			
		||||
+		cell-index = <4>;
 | 
			
		||||
 		reg = <0x16300000 0x100>;
 | 
			
		||||
 		clocks = <&gcc GSBI4_H_CLK>;
 | 
			
		||||
 		clock-names = "iface";
 | 
			
		||||
@@ -48,6 +52,8 @@ Example for APQ8064:
 | 
			
		||||
 		qcom,mode = <GSBI_PROT_I2C_UART>;
 | 
			
		||||
 		qcom,crci = <GSBI_CRCI_QUP>;
 | 
			
		||||
 
 | 
			
		||||
+		syscon-tcsr = <&tcsr>;
 | 
			
		||||
+
 | 
			
		||||
 		/* child nodes go under here */
 | 
			
		||||
 
 | 
			
		||||
 		i2c_qup4: i2c@16380000 {
 | 
			
		||||
@@ -76,3 +82,9 @@ Example for APQ8064:
 | 
			
		||||
 		};
 | 
			
		||||
 	};
 | 
			
		||||
 
 | 
			
		||||
+	tcsr: syscon@1a400000 {
 | 
			
		||||
+		compatible = "qcom,apq8064-tcsr", "syscon";
 | 
			
		||||
+		reg = <0x1a400000 0x100>;
 | 
			
		||||
+	};
 | 
			
		||||
+
 | 
			
		||||
+
 | 
			
		||||
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
 | 
			
		||||
index 7bd2c94..460b2db 100644
 | 
			
		||||
--- a/drivers/soc/qcom/Kconfig
 | 
			
		||||
+++ b/drivers/soc/qcom/Kconfig
 | 
			
		||||
@@ -4,6 +4,7 @@
 | 
			
		||||
 config QCOM_GSBI
 | 
			
		||||
         tristate "QCOM General Serial Bus Interface"
 | 
			
		||||
         depends on ARCH_QCOM
 | 
			
		||||
+        select MFD_SYSCON
 | 
			
		||||
         help
 | 
			
		||||
           Say y here to enable GSBI support.  The GSBI provides control
 | 
			
		||||
           functions for connecting the underlying serial UART, SPI, and I2C
 | 
			
		||||
diff --git a/drivers/soc/qcom/qcom_gsbi.c b/drivers/soc/qcom/qcom_gsbi.c
 | 
			
		||||
index 729425d..09c669e 100644
 | 
			
		||||
--- a/drivers/soc/qcom/qcom_gsbi.c
 | 
			
		||||
+++ b/drivers/soc/qcom/qcom_gsbi.c
 | 
			
		||||
@@ -18,22 +18,129 @@
 | 
			
		||||
 #include <linux/of.h>
 | 
			
		||||
 #include <linux/of_platform.h>
 | 
			
		||||
 #include <linux/platform_device.h>
 | 
			
		||||
+#include <linux/regmap.h>
 | 
			
		||||
+#include <linux/mfd/syscon.h>
 | 
			
		||||
+#include <dt-bindings/soc/qcom,gsbi.h>
 | 
			
		||||
 
 | 
			
		||||
 #define GSBI_CTRL_REG		0x0000
 | 
			
		||||
 #define GSBI_PROTOCOL_SHIFT	4
 | 
			
		||||
+#define MAX_GSBI		12
 | 
			
		||||
+
 | 
			
		||||
+#define TCSR_ADM_CRCI_BASE	0x70
 | 
			
		||||
+
 | 
			
		||||
+struct crci_config {
 | 
			
		||||
+	u32 num_rows;
 | 
			
		||||
+	const u32 (*array)[MAX_GSBI];
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+static const u32 crci_ipq8064[][MAX_GSBI] = {
 | 
			
		||||
+	{
 | 
			
		||||
+		0x000003, 0x00000c, 0x000030, 0x0000c0,
 | 
			
		||||
+		0x000300, 0x000c00, 0x003000, 0x00c000,
 | 
			
		||||
+		0x030000, 0x0c0000, 0x300000, 0xc00000
 | 
			
		||||
+	},
 | 
			
		||||
+	{
 | 
			
		||||
+		0x000003, 0x00000c, 0x000030, 0x0000c0,
 | 
			
		||||
+		0x000300, 0x000c00, 0x003000, 0x00c000,
 | 
			
		||||
+		0x030000, 0x0c0000, 0x300000, 0xc00000
 | 
			
		||||
+	},
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+static const struct crci_config config_ipq8064 = {
 | 
			
		||||
+	.num_rows = ARRAY_SIZE(crci_ipq8064),
 | 
			
		||||
+	.array = crci_ipq8064,
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+static const unsigned int crci_apq8064[][MAX_GSBI] = {
 | 
			
		||||
+	{
 | 
			
		||||
+		0x001800, 0x006000, 0x000030, 0x0000c0,
 | 
			
		||||
+		0x000300, 0x000400, 0x000000, 0x000000,
 | 
			
		||||
+		0x000000, 0x000000, 0x000000, 0x000000
 | 
			
		||||
+	},
 | 
			
		||||
+	{
 | 
			
		||||
+		0x000000, 0x000000, 0x000000, 0x000000,
 | 
			
		||||
+		0x000000, 0x000020, 0x0000c0, 0x000000,
 | 
			
		||||
+		0x000000, 0x000000, 0x000000, 0x000000
 | 
			
		||||
+	},
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+static const struct crci_config config_apq8064 = {
 | 
			
		||||
+	.num_rows = ARRAY_SIZE(crci_apq8064),
 | 
			
		||||
+	.array = crci_apq8064,
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+static const unsigned int crci_msm8960[][MAX_GSBI] = {
 | 
			
		||||
+	{
 | 
			
		||||
+		0x000003, 0x00000c, 0x000030, 0x0000c0,
 | 
			
		||||
+		0x000300, 0x000400, 0x000000, 0x000000,
 | 
			
		||||
+		0x000000, 0x000000, 0x000000, 0x000000
 | 
			
		||||
+	},
 | 
			
		||||
+	{
 | 
			
		||||
+		0x000000, 0x000000, 0x000000, 0x000000,
 | 
			
		||||
+		0x000000, 0x000020, 0x0000c0, 0x000300,
 | 
			
		||||
+		0x001800, 0x006000, 0x000000, 0x000000
 | 
			
		||||
+	},
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+static const struct crci_config config_msm8960 = {
 | 
			
		||||
+	.num_rows = ARRAY_SIZE(crci_msm8960),
 | 
			
		||||
+	.array = crci_msm8960,
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+static const unsigned int crci_msm8660[][MAX_GSBI] = {
 | 
			
		||||
+	{	/* ADM 0 - B */
 | 
			
		||||
+		0x000003, 0x00000c, 0x000030, 0x0000c0,
 | 
			
		||||
+		0x000300, 0x000c00, 0x003000, 0x00c000,
 | 
			
		||||
+		0x030000, 0x0c0000, 0x300000, 0xc00000
 | 
			
		||||
+	},
 | 
			
		||||
+	{	/* ADM 0 - B */
 | 
			
		||||
+		0x000003, 0x00000c, 0x000030, 0x0000c0,
 | 
			
		||||
+		0x000300, 0x000c00, 0x003000, 0x00c000,
 | 
			
		||||
+		0x030000, 0x0c0000, 0x300000, 0xc00000
 | 
			
		||||
+	},
 | 
			
		||||
+	{	/* ADM 1 - A */
 | 
			
		||||
+		0x000003, 0x00000c, 0x000030, 0x0000c0,
 | 
			
		||||
+		0x000300, 0x000c00, 0x003000, 0x00c000,
 | 
			
		||||
+		0x030000, 0x0c0000, 0x300000, 0xc00000
 | 
			
		||||
+	},
 | 
			
		||||
+	{	/* ADM 1 - B */
 | 
			
		||||
+		0x000003, 0x00000c, 0x000030, 0x0000c0,
 | 
			
		||||
+		0x000300, 0x000c00, 0x003000, 0x00c000,
 | 
			
		||||
+		0x030000, 0x0c0000, 0x300000, 0xc00000
 | 
			
		||||
+	},
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+static const struct crci_config config_msm8660 = {
 | 
			
		||||
+	.num_rows = ARRAY_SIZE(crci_msm8660),
 | 
			
		||||
+	.array = crci_msm8660,
 | 
			
		||||
+};
 | 
			
		||||
 
 | 
			
		||||
 struct gsbi_info {
 | 
			
		||||
 	struct clk *hclk;
 | 
			
		||||
 	u32 mode;
 | 
			
		||||
 	u32 crci;
 | 
			
		||||
+	struct regmap *tcsr;
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+static const struct of_device_id tcsr_dt_match[] = {
 | 
			
		||||
+	{ .compatible = "qcom,tcsr-ipq8064", .data = &config_ipq8064},
 | 
			
		||||
+	{ .compatible = "qcom,tcsr-apq8064", .data = &config_apq8064},
 | 
			
		||||
+	{ .compatible = "qcom,tcsr-msm8960", .data = &config_msm8960},
 | 
			
		||||
+	{ .compatible = "qcom,tcsr-msm8660", .data = &config_msm8660},
 | 
			
		||||
+	{ },
 | 
			
		||||
 };
 | 
			
		||||
 
 | 
			
		||||
 static int gsbi_probe(struct platform_device *pdev)
 | 
			
		||||
 {
 | 
			
		||||
 	struct device_node *node = pdev->dev.of_node;
 | 
			
		||||
+	struct device_node *tcsr_node;
 | 
			
		||||
+	const struct of_device_id *match;
 | 
			
		||||
 	struct resource *res;
 | 
			
		||||
 	void __iomem *base;
 | 
			
		||||
 	struct gsbi_info *gsbi;
 | 
			
		||||
+	int i;
 | 
			
		||||
+	u32 mask, gsbi_num;
 | 
			
		||||
+	const struct crci_config *config = NULL;
 | 
			
		||||
 
 | 
			
		||||
 	gsbi = devm_kzalloc(&pdev->dev, sizeof(*gsbi), GFP_KERNEL);
 | 
			
		||||
 
 | 
			
		||||
@@ -45,6 +152,32 @@ static int gsbi_probe(struct platform_device *pdev)
 | 
			
		||||
 	if (IS_ERR(base))
 | 
			
		||||
 		return PTR_ERR(base);
 | 
			
		||||
 
 | 
			
		||||
+	/* get the tcsr node and setup the config and regmap */
 | 
			
		||||
+	gsbi->tcsr = syscon_regmap_lookup_by_phandle(node, "syscon-tcsr");
 | 
			
		||||
+
 | 
			
		||||
+	if (!IS_ERR(gsbi->tcsr)) {
 | 
			
		||||
+		tcsr_node = of_parse_phandle(node, "syscon-tcsr", 0);
 | 
			
		||||
+		if (tcsr_node) {
 | 
			
		||||
+			match = of_match_node(tcsr_dt_match, tcsr_node);
 | 
			
		||||
+			if (match)
 | 
			
		||||
+				config = match->data;
 | 
			
		||||
+			else
 | 
			
		||||
+				dev_warn(&pdev->dev, "no matching TCSR\n");
 | 
			
		||||
+
 | 
			
		||||
+			of_node_put(tcsr_node);
 | 
			
		||||
+		}
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	if (of_property_read_u32(node, "cell-index", &gsbi_num)) {
 | 
			
		||||
+		dev_err(&pdev->dev, "missing cell-index\n");
 | 
			
		||||
+		return -EINVAL;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	if (gsbi_num < 1 || gsbi_num > MAX_GSBI) {
 | 
			
		||||
+		dev_err(&pdev->dev, "invalid cell-index\n");
 | 
			
		||||
+		return -EINVAL;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
 	if (of_property_read_u32(node, "qcom,mode", &gsbi->mode)) {
 | 
			
		||||
 		dev_err(&pdev->dev, "missing mode configuration\n");
 | 
			
		||||
 		return -EINVAL;
 | 
			
		||||
@@ -64,6 +197,25 @@ static int gsbi_probe(struct platform_device *pdev)
 | 
			
		||||
 	writel_relaxed((gsbi->mode << GSBI_PROTOCOL_SHIFT) | gsbi->crci,
 | 
			
		||||
 				base + GSBI_CTRL_REG);
 | 
			
		||||
 
 | 
			
		||||
+	/*
 | 
			
		||||
+	 * modify tcsr to reflect mode and ADM CRCI mux
 | 
			
		||||
+	 * Each gsbi contains a pair of bits, one for RX and one for TX
 | 
			
		||||
+	 * SPI mode requires both bits cleared, otherwise they are set
 | 
			
		||||
+	 */
 | 
			
		||||
+	if (config) {
 | 
			
		||||
+		for (i = 0; i < config->num_rows; i++) {
 | 
			
		||||
+			mask = config->array[i][gsbi_num - 1];
 | 
			
		||||
+
 | 
			
		||||
+			if (gsbi->mode == GSBI_PROT_SPI)
 | 
			
		||||
+				regmap_update_bits(gsbi->tcsr,
 | 
			
		||||
+					TCSR_ADM_CRCI_BASE + 4 * i, mask, 0);
 | 
			
		||||
+			else
 | 
			
		||||
+				regmap_update_bits(gsbi->tcsr,
 | 
			
		||||
+					TCSR_ADM_CRCI_BASE + 4 * i, mask, mask);
 | 
			
		||||
+
 | 
			
		||||
+		}
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
 	/* make sure the gsbi control write is not reordered */
 | 
			
		||||
 	wmb();
 | 
			
		||||
 
 | 
			
		||||
-- 
 | 
			
		||||
1.9.1
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,65 @@
 | 
			
		||||
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
 | 
			
		||||
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
 | 
			
		||||
@@ -132,6 +132,7 @@
 | 
			
		||||
 
 | 
			
		||||
 		gsbi2: gsbi@12480000 {
 | 
			
		||||
 			compatible = "qcom,gsbi-v1.0.0";
 | 
			
		||||
+			cell-index = <2>;
 | 
			
		||||
 			reg = <0x12480000 0x100>;
 | 
			
		||||
 			clocks = <&gcc GSBI2_H_CLK>;
 | 
			
		||||
 			clock-names = "iface";
 | 
			
		||||
@@ -140,6 +141,8 @@
 | 
			
		||||
 			ranges;
 | 
			
		||||
 			status = "disabled";
 | 
			
		||||
 
 | 
			
		||||
+			syscon-tcsr = <&tcsr>;
 | 
			
		||||
+
 | 
			
		||||
 			uart2: serial@12490000 {
 | 
			
		||||
 				compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
 | 
			
		||||
 				reg = <0x12490000 0x1000>,
 | 
			
		||||
@@ -167,6 +170,7 @@
 | 
			
		||||
 
 | 
			
		||||
 		gsbi4: gsbi@16300000 {
 | 
			
		||||
 			compatible = "qcom,gsbi-v1.0.0";
 | 
			
		||||
+			cell-index = <4>;
 | 
			
		||||
 			reg = <0x16300000 0x100>;
 | 
			
		||||
 			clocks = <&gcc GSBI4_H_CLK>;
 | 
			
		||||
 			clock-names = "iface";
 | 
			
		||||
@@ -175,6 +179,8 @@
 | 
			
		||||
 			ranges;
 | 
			
		||||
 			status = "disabled";
 | 
			
		||||
 
 | 
			
		||||
+			syscon-tcsr = <&tcsr>;
 | 
			
		||||
+
 | 
			
		||||
 			uart4: serial@16340000 {
 | 
			
		||||
 				compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
 | 
			
		||||
 				reg = <0x16340000 0x1000>,
 | 
			
		||||
@@ -201,6 +207,7 @@
 | 
			
		||||
 
 | 
			
		||||
 		gsbi5: gsbi@1a200000 {
 | 
			
		||||
 			compatible = "qcom,gsbi-v1.0.0";
 | 
			
		||||
+			cell-index = <5>;
 | 
			
		||||
 			reg = <0x1a200000 0x100>;
 | 
			
		||||
 			clocks = <&gcc GSBI5_H_CLK>;
 | 
			
		||||
 			clock-names = "iface";
 | 
			
		||||
@@ -209,6 +216,8 @@
 | 
			
		||||
 			ranges;
 | 
			
		||||
 			status = "disabled";
 | 
			
		||||
 
 | 
			
		||||
+			syscon-tcsr = <&tcsr>;
 | 
			
		||||
+
 | 
			
		||||
 			uart5: serial@1a240000 {
 | 
			
		||||
 				compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
 | 
			
		||||
 				reg = <0x1a240000 0x1000>,
 | 
			
		||||
@@ -279,6 +288,11 @@
 | 
			
		||||
 			status = "disabled";
 | 
			
		||||
 		};
 | 
			
		||||
 
 | 
			
		||||
+		tcsr: syscon@1a400000 {
 | 
			
		||||
+			compatible = "qcom,tcsr-ipq8064", "syscon";
 | 
			
		||||
+			reg = <0x1a400000 0x100>;
 | 
			
		||||
+		};
 | 
			
		||||
+
 | 
			
		||||
 		qcom,ssbi@500000 {
 | 
			
		||||
 			compatible = "qcom,ssbi";
 | 
			
		||||
 			reg = <0x00500000 0x1000>;
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
 | 
			
		||||
+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
 | 
			
		||||
@@ -18,8 +18,15 @@
 | 
			
		||||
 		bootargs = "console=ttyMSM0,115200 root=/dev/mtdblock12 rootfstype=squashfs,jffs2";
 | 
			
		||||
@@ -22,8 +22,15 @@
 | 
			
		||||
 		linux,stdout-path = "serial0:115200n8";
 | 
			
		||||
 	};
 | 
			
		||||
 
 | 
			
		||||
+	aliases {
 | 
			
		||||
@@ -16,7 +16,7 @@
 | 
			
		||||
 			i2c4_pins: i2c4_pinmux {
 | 
			
		||||
 				pins = "gpio12", "gpio13";
 | 
			
		||||
 				function = "gsbi4";
 | 
			
		||||
@@ -34,6 +41,25 @@
 | 
			
		||||
@@ -38,6 +45,25 @@
 | 
			
		||||
 					bias-none;
 | 
			
		||||
 				};
 | 
			
		||||
 			};
 | 
			
		||||
@@ -42,16 +42,16 @@
 | 
			
		||||
 		};
 | 
			
		||||
 
 | 
			
		||||
 		gsbi@16300000 {
 | 
			
		||||
@@ -72,6 +98,7 @@
 | 
			
		||||
@@ -76,6 +102,7 @@
 | 
			
		||||
 					#size-cells = <1>;
 | 
			
		||||
 					spi-max-frequency = <50000000>;
 | 
			
		||||
 					reg = <0>;
 | 
			
		||||
+					m25p,fast-read;
 | 
			
		||||
 
 | 
			
		||||
 					partition@0 {
 | 
			
		||||
 						label = "0:SBL1";
 | 
			
		||||
@@ -148,5 +175,66 @@
 | 
			
		||||
 		sata@29000000 {
 | 
			
		||||
 						label = "lowlevel_init";
 | 
			
		||||
@@ -136,5 +163,66 @@
 | 
			
		||||
 		usb30@1 {
 | 
			
		||||
 			status = "ok";
 | 
			
		||||
 		};
 | 
			
		||||
+
 | 
			
		||||
@@ -127,11 +127,10 @@
 | 
			
		||||
 
 | 
			
		||||
 / {
 | 
			
		||||
 	model = "Qualcomm IPQ8064";
 | 
			
		||||
@@ -279,5 +280,42 @@
 | 
			
		||||
 			#clock-cells = <1>;
 | 
			
		||||
 			#reset-cells = <1>;
 | 
			
		||||
@@ -390,5 +391,41 @@
 | 
			
		||||
 			};
 | 
			
		||||
+
 | 
			
		||||
 		};
 | 
			
		||||
 
 | 
			
		||||
+		nss-gmac-common {
 | 
			
		||||
+			reg = <0x03000000 0x0000FFFF 0x1bb00000 0x0000FFFF 0x00900000 0x00004000>;
 | 
			
		||||
+			reg-names = "nss_reg_base" , "qsgmii_reg_base", "clk_ctl_base";
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user