linux-arm-msm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] phy: USB and PCIe phy drivers for Qcom chipsets
@ 2016-10-19 10:43 Vivek Gautam
  2016-10-19 10:43 ` [PATCH 1/2] phy: qcom-qusb2: New driver for QUSB2 PHY on Qcom chips Vivek Gautam
  2016-10-19 10:43 ` [PATCH 2/2] phy: qcom-qmp: new qmp phy driver for qcom-chipsets Vivek Gautam
  0 siblings, 2 replies; 19+ messages in thread
From: Vivek Gautam @ 2016-10-19 10:43 UTC (permalink / raw)
  To: kishon-l0cyMroinI0, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA, Vivek Gautam

This patch series adds couple of PHY drivers for Qualcomm chipsets.
a) qcom-qusb2 phy driver: that provides High Speed USB functionality.
b) qcom-qmp phy driver: that is a combo phy providing support for
   USB3, PCIe and UFS controllers.[1]

These drivers are based on the work for msm that is available in
codeaurora[2].

The patches are based on next branch of linux-phy tree.

These have been tested on Dragon board db820c hardware with
required set of patches on integration tree maintained by
Linaro landing team for Qualcomm [3].

[1] Currently the qcom-qmp phy driver supports only USB3 and PCIe
controllers. Later, we plan to add the UFS phy support as well to this.
[2] https://source.codeaurora.org/quic/la/kernel/msm-4.4/log/?h=caf/3.18/msm-3.18
[3] https://git.linaro.org/?p=landing-teams/working/qualcomm/kernel.git;a=shortlog;h=refs/heads/integration-linux-qcomlt

Vivek Gautam (2):
  phy: qcom-qusb2: New driver for QUSB2 PHY on Qcom chips
  phy: qcom-qmp: new qmp phy driver for qcom-chipsets

 .../devicetree/bindings/phy/qcom-qmp-phy.txt       |   61 ++
 .../devicetree/bindings/phy/qcom-qusb2-phy.txt     |   37 +
 drivers/phy/Kconfig                                |   18 +
 drivers/phy/Makefile                               |    2 +
 drivers/phy/phy-qcom-qmp.c                         | 1154 ++++++++++++++++++++
 drivers/phy/phy-qcom-qusb2.c                       |  577 ++++++++++
 6 files changed, 1849 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
 create mode 100644 Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt
 create mode 100644 drivers/phy/phy-qcom-qmp.c
 create mode 100644 drivers/phy/phy-qcom-qusb2.c

-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 1/2] phy: qcom-qusb2: New driver for QUSB2 PHY on Qcom chips
  2016-10-19 10:43 [PATCH 0/2] phy: USB and PCIe phy drivers for Qcom chipsets Vivek Gautam
@ 2016-10-19 10:43 ` Vivek Gautam
  2016-10-20  4:13   ` Vivek Gautam
                     ` (2 more replies)
  2016-10-19 10:43 ` [PATCH 2/2] phy: qcom-qmp: new qmp phy driver for qcom-chipsets Vivek Gautam
  1 sibling, 3 replies; 19+ messages in thread
From: Vivek Gautam @ 2016-10-19 10:43 UTC (permalink / raw)
  To: kishon, robh+dt, mark.rutland
  Cc: devicetree, linux-kernel, linux-arm-msm, Vivek Gautam

PHY transceiver driver for QUSB2 phy controller that provides
HighSpeed functionality for DWC3 controller present on
Qualcomm chipsets.

This driver is based on phy-msm-qusb driver available in
msm-4.4 kernel @codeaurora[1]

[1] https://source.codeaurora.org/quic/la/kernel/msm-4.4/log/?h=caf/3.18/msm-3.18

Signed-off-by: Vivek Gautam <vivek.gautam@codeaurora.org>
Cc: Kishon Vijay Abraham I <kishon@ti.com>
---
 .../devicetree/bindings/phy/qcom-qusb2-phy.txt     |  37 ++
 drivers/phy/Kconfig                                |  10 +
 drivers/phy/Makefile                               |   1 +
 drivers/phy/phy-qcom-qusb2.c                       | 577 +++++++++++++++++++++
 4 files changed, 625 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt
 create mode 100644 drivers/phy/phy-qcom-qusb2.c

diff --git a/Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt b/Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt
new file mode 100644
index 0000000..97c9ce7
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt
@@ -0,0 +1,37 @@
+Qualcomm QUSB2 phy controller
+=============================
+
+QUSB2 controller supports LS/FS/HS usb connectivity on Qualcomm chipsets.
+
+Required properties:
+ - compatible: compatible list, contains "qcom,msm8996-qusb2-phy".
+ - reg: offset and length of the PHY register set.
+ - #phy-cells: must be 0.
+
+ - clocks: a list of phandles and clock-specifier pairs,
+	   one for each entry in clock-names.
+ - clock-names: must be "cfg_ahb" for phy config clock,
+			"ref_clk" for 19.2 MHz ref clk,
+			"ref_clk_src" reference clock source.
+			"iface" for phy interface clock (Optional).
+
+ - vdd-phy-supply: Phandle to a regulator supply to PHY core block.
+ - vdda-pll-supply: Phandle to 1.8V regulator supply to PHY refclk pll block.
+ - vdda-phy-dpdm: Phandle to 3.1V regulator supply to Dp/Dm port signals.
+
+ - resets: a list of phandles and reset controller specifier pairs,
+	   one for each entry in reset-names.
+ - reset-names: must be "phy" for reset of phy block.
+
+Optional properties:
+ - nvmem-cells: a list of phandles to nvmem cells that contain fused
+		tuning parameters for qusb2 phy, one for each entry
+		in nvmem-cell-names.
+ - nvmem-cell-names: must be "tune2_hstx_trim_efuse" for cell containing
+		     HS Tx trim value.
+ - qcom,hstx-trim-bit-offset: bit offset within nvmem cell for
+			      HS Tx trim value.
+ - qcom,hstx-trim-bit-len: bit length of HS Tx trim value within nvmem cell.
+
+ - qcom,tcsr-syscon: Phandle to TCSR syscon register region.
+ - qcom,phy-clk-scheme: Offset to TCSR_PHY_CLK_SCHEME_SEL register.
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index fe00f91..5547984 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -446,6 +446,16 @@ config PHY_STIH41X_USB
 	  Enable this to support the USB transceiver that is part of
 	  STMicroelectronics STiH41x SoC series.
 
+config PHY_QCOM_QUSB2
+	tristate "Qualcomm QUSB2 PHY Driver"
+	depends on OF && (ARCH_QCOM || COMPILE_TEST)
+	select GENERIC_PHY
+	help
+	  Enable this to support the HighSpeed QUSB2 PHY transceiver for USB
+	  controllers on Qualcomm chips. This driver supports the high-speed
+	  PHY which is usually paired with either the ChipIdea or Synopsys DWC3
+	  USB IPs on MSM SOCs.
+
 config PHY_QCOM_UFS
 	tristate "Qualcomm UFS PHY driver"
 	depends on OF && ARCH_QCOM
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index a534cf5..848489d 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+= phy-spear1340-miphy.o
 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_QUSB2) 	+= phy-qcom-qusb2.o
 obj-$(CONFIG_PHY_QCOM_UFS) 	+= phy-qcom-ufs.o
 obj-$(CONFIG_PHY_QCOM_UFS) 	+= phy-qcom-ufs-qmp-20nm.o
 obj-$(CONFIG_PHY_QCOM_UFS) 	+= phy-qcom-ufs-qmp-14nm.o
diff --git a/drivers/phy/phy-qcom-qusb2.c b/drivers/phy/phy-qcom-qusb2.c
new file mode 100644
index 0000000..058f661
--- /dev/null
+++ b/drivers/phy/phy-qcom-qusb2.c
@@ -0,0 +1,577 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. 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/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+#define QUSB2PHY_PLL_TEST		0x04
+#define CLK_REF_SEL			BIT(7)
+
+#define QUSB2PHY_PLL_TUNE		0x08
+#define QUSB2PHY_PLL_USER_CTL1		0x0c
+#define QUSB2PHY_PLL_USER_CTL2		0x10
+#define QUSB2PHY_PLL_AUTOPGM_CTL1	0x1c
+#define QUSB2PHY_PLL_PWR_CTRL		0x18
+
+#define QUSB2PHY_PLL_STATUS		0x38
+#define PLL_LOCKED			BIT(5)
+
+#define QUSB2PHY_PORT_TUNE1             0x80
+
+#define QUSB2PHY_PORT_TUNE2             0x84
+/* TUNE2's high nibble value read from efuse */
+#define TUNE2_HIGH_NIBBLE_VAL(val, pos, mask)	((val >> pos) & mask)
+
+#define QUSB2PHY_PORT_TUNE3             0x88
+#define QUSB2PHY_PORT_TUNE4             0x8C
+#define QUSB2PHY_PORT_TUNE5		0x90
+#define QUSB2PHY_PORT_TEST2		0x9c
+
+#define QUSB2PHY_PORT_POWERDOWN		0xB4
+#define CLAMP_N_EN			BIT(5)
+#define FREEZIO_N			BIT(1)
+#define POWER_DOWN			BIT(0)
+
+#define QUSB2PHY_REFCLK_ENABLE		BIT(0)
+
+#define PHY_CLK_SCHEME_SEL		BIT(0)
+
+struct qusb2_phy_init_tbl {
+	unsigned int reg_offset;
+	unsigned int cfg_val;
+};
+#define QCOM_QUSB2_PHY_INIT_CFG(reg, val) \
+	{				\
+		.reg_offset = reg,	\
+		.cfg_val = val,		\
+	}
+
+static struct qusb2_phy_init_tbl msm8996_phy_init_tbl[] = {
+	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE1, 0xF8),
+	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE2, 0xB3),
+	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE3, 0x83),
+	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE4, 0xC0),
+	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30),
+	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79),
+	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21),
+	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TEST2, 0x14),
+	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9F),
+	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00),
+};
+
+struct qusb2_phy_init_cfg {
+	struct qusb2_phy_init_tbl *phy_init_tbl;
+	int phy_init_tbl_sz;
+};
+
+const struct qusb2_phy_init_cfg msm8996_phy_init_cfg = {
+	.phy_init_tbl = msm8996_phy_init_tbl,
+	.phy_init_tbl_sz = ARRAY_SIZE(msm8996_phy_init_tbl),
+};
+
+/**
+ * struct qusb2_phy: Structure holding qusb2 phy attributes.
+ *
+ * @phy: pointer to generic phy.
+ * @base: pointer to iomapped memory space for qubs2 phy.
+ *
+ * @cfg_ahb_clk: pointer to AHB2PHY interface clock.
+ * @ref_clk: pointer to reference clock.
+ * @ref_clk_src: pointer to source to reference clock.
+ * @iface_src: pointer to phy interface clock.
+ * @clk_enabled: check if clocks are enabled.
+ *
+ * @phy_reset: Pointer to phy reset control
+ *
+ * @vdda_phy: vdd supply to the phy core block.
+ * @vdda_pll: 1.8V vdd supply to ref_clk block.
+ * @vdda_phy_dpdm: 3.1V vdd supply to Dp/Dm port signals.
+ * @pwr_enabled: check if the regulators are enabled.
+ * @tcsr: pointer to TCSR syscon register map.
+ * @clk_scheme_offset: offset to PHY_CLK_SCHEME register in TCSR map.
+ * @cfg: phy initialization config data
+ */
+struct qusb2_phy {
+	struct phy *phy;
+	void __iomem *base;
+
+	struct clk *cfg_ahb_clk;
+	struct clk *ref_clk;
+	struct clk *ref_clk_src;
+	struct clk *iface_clk;
+	bool clk_enabled;
+
+	struct reset_control *phy_reset;
+
+	struct regulator *vdd_phy;
+	struct regulator *vdda_pll;
+	struct regulator *vdda_phy_dpdm;
+	bool pwr_enabled;
+
+	struct regmap *tcsr;
+	unsigned int clk_scheme_offset;
+
+	const struct qusb2_phy_init_cfg *cfg;
+};
+
+static inline void qusb2_set_bits(void __iomem *reg, u32 mask)
+{
+	u32 val;
+
+	val = readl_relaxed(reg);
+	val |= mask;
+	writel_relaxed(val, reg);
+
+	/* Ensure above write is completed */
+	mb();
+}
+
+static inline void qusb2_clr_bits(void __iomem *reg, u32 mask)
+{
+	u32 val;
+
+	val = readl_relaxed(reg);
+	val &= ~mask;
+	writel_relaxed(val, reg);
+
+	/* Ensure above write is completed */
+	mb();
+}
+
+static void qcom_qusb2_phy_configure(void __iomem *base,
+				struct qusb2_phy_init_tbl init_tbl[],
+				int init_tbl_sz)
+{
+	int i;
+
+	for (i = 0; i < init_tbl_sz; i++) {
+		writel_relaxed(init_tbl[i].cfg_val,
+				base + init_tbl[i].reg_offset);
+	}
+
+	/* flush buffered writes */
+	mb();
+}
+
+static void qusb2_phy_enable_clocks(struct qusb2_phy *qphy, bool on)
+{
+	dev_dbg(&qphy->phy->dev, "%s(): clocks_enabled:%d on:%d\n",
+			__func__, qphy->clk_enabled, on);
+
+	if (!qphy->clk_enabled && on) {
+		clk_prepare_enable(qphy->cfg_ahb_clk);
+		if (qphy->iface_clk)
+			clk_prepare_enable(qphy->iface_clk);
+		clk_prepare_enable(qphy->ref_clk_src);
+		qphy->clk_enabled = true;
+	}
+
+	if (qphy->clk_enabled && !on) {
+		clk_disable_unprepare(qphy->ref_clk_src);
+		if (qphy->iface_clk)
+			clk_disable_unprepare(qphy->iface_clk);
+		clk_disable_unprepare(qphy->cfg_ahb_clk);
+		qphy->clk_enabled = false;
+	}
+
+	dev_dbg(&qphy->phy->dev, "%s(): clocks_enabled:%d\n", __func__,
+						qphy->clk_enabled);
+}
+
+static int qusb2_phy_enable_power(struct qusb2_phy *qphy, bool on)
+{
+	int ret = 0;
+	struct device *dev = &qphy->phy->dev;
+
+	dev_dbg(dev, "%s(): turn %s regulators. power_enabled:%d\n",
+			__func__, on ? "on" : "off", qphy->pwr_enabled);
+
+	if (qphy->pwr_enabled == on)
+		return 0;
+
+	if (!on)
+		goto disable_vdda_phy_dpdm;
+
+	ret = regulator_enable(qphy->vdd_phy);
+	if (ret) {
+		dev_err(dev, "Unable to enable vdd-phy:%d\n", ret);
+		goto err_vdd_phy;
+	}
+
+	ret = regulator_enable(qphy->vdda_pll);
+	if (ret) {
+		dev_err(dev, "Unable to enable vdda-pll:%d\n", ret);
+		goto disable_vdd_phy;
+	}
+
+	ret = regulator_enable(qphy->vdda_phy_dpdm);
+	if (ret) {
+		dev_err(dev, "Unable to enable vdda-phy-dpdm:%d\n", ret);
+		goto disable_vdda_pll;
+	}
+
+	qphy->pwr_enabled = true;
+	dev_dbg(dev, "%s() regulators are turned on.\n", __func__);
+
+	return ret;
+
+disable_vdda_phy_dpdm:
+	regulator_disable(qphy->vdda_phy_dpdm);
+disable_vdda_pll:
+	regulator_disable(qphy->vdda_pll);
+disable_vdd_phy:
+	regulator_disable(qphy->vdd_phy);
+err_vdd_phy:
+	qphy->pwr_enabled = false;
+	dev_dbg(dev, "%s() regulators are turned off.\n", __func__);
+	return ret;
+}
+
+/*
+ * Fetches HS Tx tuning value from e-fuse and sets QUSB2PHY_PORT_TUNE2
+ * register.
+ * For any error case, skip setting the value and use the default value.
+ */
+static int qusb2_phy_set_tune2_param(struct qusb2_phy *qphy)
+{
+	struct device *dev = &qphy->phy->dev;
+	struct nvmem_cell *cell;
+	unsigned int bit_mask;
+	u32 tune2_val;
+	int bit_offset = 0;
+	int bit_len = 0;
+	ssize_t len;
+	u32 *val;
+	int ret;
+
+	/*
+	 * Read EFUSE register having TUNE2 parameter's high nibble.
+	 * If efuse register shows value as 0x0, or if we fail to find
+	 * a valid efuse register settings, then use default value
+	 * as 0xB for high nibble that we have already set while
+	 * configuring phy.
+	 */
+	cell = devm_nvmem_cell_get(dev, "tune2_hstx_trim_efuse");
+	if (IS_ERR(cell)) {
+		if (PTR_ERR(cell) == -EPROBE_DEFER)
+			return PTR_ERR(cell);
+		goto skip;
+	}
+
+	val = (u32 *)nvmem_cell_read(cell, &len);
+	if (!IS_ERR(val)) {
+		ret = of_property_read_u32(dev->of_node,
+						"qcom,hstx-trim-bit-offset",
+						&bit_offset);
+		if (ret) {
+			dev_dbg(dev, "hstx-trim bit offset is invalid.\n");
+			goto skip;
+		}
+
+		ret = of_property_read_u32(dev->of_node,
+						"qcom,hstx-trim-bit-len",
+						&bit_len);
+		if (ret) {
+			dev_dbg(dev, "hstx-trim bit length is invalid.\n");
+			goto skip;
+		}
+
+		bit_mask = (1 << bit_len) - 1;
+		dev_dbg(dev,
+			"%s(): efuse value:%x, bit_mask:%x, bit_offset: %d\n",
+			__func__, val[0], bit_mask, bit_offset);
+		tune2_val = TUNE2_HIGH_NIBBLE_VAL(val[0], bit_offset, bit_mask);
+		if (!tune2_val) {
+			dev_dbg(dev, "hstx-trim bit length is invalid.\n");
+			goto skip;
+		}
+
+		/* Fused TUNE2 value is the higher nibble only */
+		tune2_val = tune2_val << 0x4;
+		qusb2_set_bits(qphy->base + QUSB2PHY_PORT_TUNE2, tune2_val);
+	} else {
+		dev_dbg(dev, "failed reading hs-tx trim value: %d\n", ret);
+	}
+
+skip:
+	return 0;
+}
+
+static int qusb2_phy_init(struct phy *phy)
+{
+	struct qusb2_phy *qphy = phy_get_drvdata(phy);
+	unsigned int reset_val;
+	unsigned int clk_scheme;
+	bool is_se_clk = false;
+	int ret;
+
+	dev_info(&phy->dev, "Initializing QUSB2 phy\n");
+
+	ret = qusb2_phy_enable_power(qphy, true);
+	if (ret)
+		return ret;
+
+	qusb2_phy_enable_clocks(qphy, true);
+
+	/* Perform phy reset */
+	ret = reset_control_assert(qphy->phy_reset);
+	if (ret) {
+		dev_err(&phy->dev, "Failed to assert phy_reset\n");
+		return ret;
+	}
+	usleep_range(100, 150);
+	ret = reset_control_deassert(qphy->phy_reset);
+	if (ret) {
+		dev_err(&phy->dev, "Failed to de-assert phy_reset\n");
+		return ret;
+	}
+
+	/* Disable the PHY */
+	qusb2_set_bits(qphy->base + QUSB2PHY_PORT_POWERDOWN,
+			CLAMP_N_EN | FREEZIO_N | POWER_DOWN);
+
+	/* save reset value to override based on clk scheme */
+	reset_val = readl_relaxed(qphy->base + QUSB2PHY_PLL_TEST);
+
+	qcom_qusb2_phy_configure(qphy->base, qphy->cfg->phy_init_tbl,
+				qphy->cfg->phy_init_tbl_sz);
+
+	/* Check for efuse value for tuning the PHY */
+	ret = qusb2_phy_set_tune2_param(qphy);
+	if (ret)
+		return ret;
+
+	/* Enable the PHY */
+	qusb2_clr_bits(qphy->base + QUSB2PHY_PORT_POWERDOWN, POWER_DOWN);
+
+	/* Require to get phy pll lock successfully */
+	usleep_range(150, 160);
+
+	/*
+	 * Use differential clk by default; later we read TCSR_PHY_CLK_SCHEME
+	 * register to check if Single-ended clock scheme is selected. If yes,
+	 * then disable differential ref_clk and use single-ended clock.
+	 */
+
+	if (qphy->tcsr) {
+		ret = regmap_read(qphy->tcsr, qphy->clk_scheme_offset,
+							&clk_scheme);
+		/* is SE-clk available ? */
+		if (clk_scheme & PHY_CLK_SCHEME_SEL) {
+			dev_dbg(&phy->dev, "%s: select single-ended clk src\n",
+								__func__);
+			is_se_clk = true;
+		}
+	}
+
+	if (is_se_clk) {
+		reset_val |= CLK_REF_SEL;
+		clk_disable_unprepare(qphy->ref_clk);
+	} else {
+		reset_val &= ~CLK_REF_SEL;
+		clk_prepare_enable(qphy->ref_clk);
+	}
+
+	writel_relaxed(reset_val, qphy->base + QUSB2PHY_PLL_TEST);
+
+	/* Make sure that above write is completed to get PLL source clock */
+	wmb();
+
+	/* Required to get PHY PLL lock successfully */
+	usleep_range(100, 110);
+
+	if (!(readb_relaxed(qphy->base + QUSB2PHY_PLL_STATUS) &
+					PLL_LOCKED)) {
+		dev_err(&phy->dev, "QUSB PHY PLL LOCK fails:%x\n",
+			readb_relaxed(qphy->base + QUSB2PHY_PLL_STATUS));
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static int qusb2_phy_exit(struct phy *phy)
+{
+	struct qusb2_phy *qphy = phy_get_drvdata(phy);
+
+	/* Disable the PHY */
+	qusb2_set_bits(qphy->base + QUSB2PHY_PORT_POWERDOWN,
+			CLAMP_N_EN | FREEZIO_N | POWER_DOWN);
+
+	qusb2_phy_enable_clocks(qphy, false);
+	qusb2_phy_enable_power(qphy, false);
+
+	return 0;
+}
+
+static const struct phy_ops qusb2_phy_gen_ops = {
+	.init		= qusb2_phy_init,
+	.exit		= qusb2_phy_exit,
+	.owner		= THIS_MODULE,
+};
+
+static const struct of_device_id qusb2_phy_of_match_table[] = {
+	{
+		.compatible	= "qcom,msm8996-qusb2-phy",
+		.data		= &msm8996_phy_init_cfg,
+	},
+	{ },
+};
+MODULE_DEVICE_TABLE(of, qusb2_phy_of_match_table);
+
+static int qusb2_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct qusb2_phy *qphy;
+	struct phy_provider *phy_provider;
+	struct phy *generic_phy;
+	const struct of_device_id *match;
+	struct resource *res;
+	int ret = 0;
+
+	qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
+	if (!qphy)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+	qphy->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(qphy->base))
+		return PTR_ERR(qphy->base);
+
+	qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb_clk");
+	if (IS_ERR(qphy->cfg_ahb_clk)) {
+		ret = PTR_ERR(qphy->cfg_ahb_clk);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "failed to get cfg_ahb_clk\n");
+		return ret;
+	}
+
+	qphy->ref_clk_src = devm_clk_get(dev, "ref_clk_src");
+	if (IS_ERR(qphy->ref_clk_src)) {
+		qphy->ref_clk_src = NULL;
+		ret = PTR_ERR(qphy->ref_clk_src);
+		if (ret != -EPROBE_DEFER)
+			dev_dbg(dev, "clk get failed for ref_clk_src\n");
+	}
+
+	qphy->ref_clk = devm_clk_get(dev, "ref_clk");
+	if (IS_ERR(qphy->ref_clk)) {
+		qphy->ref_clk = NULL;
+		ret = PTR_ERR(qphy->ref_clk);
+		if (ret != -EPROBE_DEFER)
+			dev_dbg(dev, "clk get failed for ref_clk\n");
+	} else {
+		clk_set_rate(qphy->ref_clk, 19200000);
+	}
+
+	qphy->iface_clk = devm_clk_get(dev, "iface_clk");
+	if (IS_ERR(qphy->iface_clk)) {
+		qphy->iface_clk = NULL;
+		ret = PTR_ERR(qphy->iface_clk);
+		if (ret != -EPROBE_DEFER)
+			dev_dbg(dev, "clk get failed for iface_clk\n");
+	}
+
+	qphy->phy_reset = devm_reset_control_get(&pdev->dev, "phy");
+	if (IS_ERR(qphy->phy_reset)) {
+		dev_err(dev, "failed to get phy core reset\n");
+		return PTR_ERR(qphy->phy_reset);
+	}
+
+	qphy->vdd_phy = devm_regulator_get(dev, "vdd-phy");
+	if (IS_ERR(qphy->vdd_phy)) {
+		dev_err(dev, "unable to get vdd-phy supply\n");
+		return PTR_ERR(qphy->vdd_phy);
+	}
+
+	qphy->vdda_pll = devm_regulator_get(dev, "vdda-pll");
+	if (IS_ERR(qphy->vdda_pll)) {
+		dev_err(dev, "unable to get vdda-pll supply\n");
+		return PTR_ERR(qphy->vdda_pll);
+	}
+
+	qphy->vdda_phy_dpdm = devm_regulator_get(dev, "vdda-phy-dpdm");
+	if (IS_ERR(qphy->vdda_phy_dpdm)) {
+		dev_err(dev, "unable to get vdda-phy-dpdm supply\n");
+		return PTR_ERR(qphy->vdda_phy_dpdm);
+	}
+
+	/* Get the specific init parameters of QMP phy */
+	match = of_match_node(qusb2_phy_of_match_table, dev->of_node);
+	qphy->cfg = match->data;
+
+	qphy->tcsr = syscon_regmap_lookup_by_phandle(dev->of_node,
+							"qcom,tcsr-syscon");
+	if (!IS_ERR(qphy->tcsr)) {
+		ret = of_property_read_u32(dev->of_node, "qcom,phy-clk-scheme",
+						&qphy->clk_scheme_offset);
+		if (ret) {
+			dev_err(dev, "phy-clk-scheme reg offset is invalid\n");
+			qphy->tcsr = NULL;
+			return ret;
+		}
+	} else {
+		dev_dbg(dev, "Failed to lookup TCSR regmap\n");
+		qphy->tcsr = NULL;
+	}
+
+	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+	if (IS_ERR(phy_provider)) {
+		ret = PTR_ERR(phy_provider);
+		dev_err(dev, "%s: failed to register phy %d\n", __func__, ret);
+		return ret;
+	}
+
+	generic_phy = devm_phy_create(dev, NULL, &qusb2_phy_gen_ops);
+	if (IS_ERR(generic_phy)) {
+		ret = PTR_ERR(generic_phy);
+		dev_err(dev, "%s: failed to create phy %d\n", __func__, ret);
+		generic_phy = NULL;
+		return ret;
+	}
+	qphy->phy = generic_phy;
+
+	dev_set_drvdata(dev, qphy);
+	phy_set_drvdata(generic_phy, qphy);
+
+	return ret;
+}
+
+static struct platform_driver qusb2_phy_driver = {
+	.probe		= qusb2_phy_probe,
+	.driver = {
+		.name	= "qcom-qusb2-phy",
+		.of_match_table = of_match_ptr(qusb2_phy_of_match_table),
+	},
+};
+
+module_platform_driver(qusb2_phy_driver);
+
+MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>");
+MODULE_DESCRIPTION("Qualcomm QUSB2 PHY driver");
+MODULE_LICENSE("GPL v2");
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 2/2] phy: qcom-qmp: new qmp phy driver for qcom-chipsets
  2016-10-19 10:43 [PATCH 0/2] phy: USB and PCIe phy drivers for Qcom chipsets Vivek Gautam
  2016-10-19 10:43 ` [PATCH 1/2] phy: qcom-qusb2: New driver for QUSB2 PHY on Qcom chips Vivek Gautam
@ 2016-10-19 10:43 ` Vivek Gautam
  2016-10-19 10:54   ` Vivek Gautam
                     ` (2 more replies)
  1 sibling, 3 replies; 19+ messages in thread
From: Vivek Gautam @ 2016-10-19 10:43 UTC (permalink / raw)
  To: kishon, robh+dt, mark.rutland
  Cc: devicetree, linux-kernel, linux-arm-msm, Vivek Gautam

Qualcomm SOCs have QMP phy controller that provides support
to a number of controller, viz. PCIe, UFS, and USB.
Add a new driver, based on generic phy framework, for this
phy controller.

USB3-phy changes: Based on phy-msm-ssusb-qmp driver available on
msm-4.4 kernel @codeaurora[1].
PCIe-phy changes: Based on msm8996-pcie-phy driver posted by
Srinivas [2].

[1] https://source.codeaurora.org/quic/la/kernel/msm-4.4/log/?h=caf/3.18/msm-3.18
[2] https://patchwork.kernel.org/patch/9318947/

Signed-off-by: Vivek Gautam <vivek.gautam@codeaurora.org>
Cc: Kishon Vijay Abraham I <kishon@ti.com>
---
 .../devicetree/bindings/phy/qcom-qmp-phy.txt       |   61 ++
 drivers/phy/Kconfig                                |    8 +
 drivers/phy/Makefile                               |    1 +
 drivers/phy/phy-qcom-qmp.c                         | 1154 ++++++++++++++++++++
 4 files changed, 1224 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
 create mode 100644 drivers/phy/phy-qcom-qmp.c

diff --git a/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
new file mode 100644
index 0000000..90214aa
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
@@ -0,0 +1,61 @@
+Qualcomm QMP PHY
+----------------
+
+QMP phy controller supports physical layer functionality for a number of
+controllers on Qualcomm chipsets, such as, PCIe, UFS, and USB.
+
+Required properties:
+ - compatible: compatible list, contains:
+	       "qcom,msm8996-qmp-pcie-phy" for 14nm PCIe phy on msm8996,
+	       "qcom,msm8996-qmp-usb3-phy" for 14nm USB3 phy on msm8996.
+ - reg: offset and length of the PHY register set.
+ - #phy-cells: must be 1
+    - Cell after phy phandle should be the port (lane) number.
+ - clocks: a list of phandles and clock-specifier pairs,
+	   one for each entry in clock-names.
+ - clock-names: must be "cfg_ahb" for phy config clock,
+			"aux" for phy aux clock,
+			"ref_clk" for 19.2 MHz ref clk,
+			"ref_clk_src" for reference clock source,
+			"pipe<port-number>" for pipe clock specific to
+			each port/lane (Optional).
+ - resets: a list of phandles and reset controller specifier pairs,
+	   one for each entry in reset-names.
+ - reset-names: must be "phy" for reset of phy block,
+			"common" for phy common block reset,
+			"cfg" for phy's ahb cfg block reset (Optional).
+			"port<port-number>" for reset specific to
+			each port/lane. (Optional)
+ - vdda-phy-supply: Phandle to a regulator supply to PHY core block.
+ - vdda-pll-supply: Phandle to 1.8V regulator supply to PHY refclk pll block.
+
+Optional properties:
+ - vddp-ref-clk-supply: Phandle to a regulator supply to any specific refclk
+			pll block.
+
+Example:
+	pcie_phy: pciephy@34000 {
+		compatible = "qcom,qmp-14nm-pcie-phy";
+		reg = <0x034000 0x3fff>;
+		#phy-cells = <1>;
+
+		clocks = <&gcc GCC_PCIE_PHY_AUX_CLK>,
+			<&gcc GCC_PCIE_PHY_CFG_AHB_CLK>,
+			<&gcc GCC_PCIE_0_PIPE_CLK>,
+			<&gcc GCC_PCIE_1_PIPE_CLK>,
+			<&gcc GCC_PCIE_2_PIPE_CLK>;
+		clock-names = "aux", "cfg_ahb",
+				"pipe0", "pipe1", "pipe2";
+
+		vdda-phy-supply = <&pm8994_l28>;
+		vdda-pll-supply = <&pm8994_l12>;
+
+		resets = <&gcc GCC_PCIE_PHY_BCR>,
+			<&gcc GCC_PCIE_PHY_COM_BCR>,
+			<&gcc GCC_PCIE_PHY_COM_NOCSR_BCR>,
+			<&gcc GCC_PCIE_0_PHY_BCR>,
+			<&gcc GCC_PCIE_1_PHY_BCR>,
+			<&gcc GCC_PCIE_2_PHY_BCR>;
+		reset-names = "phy", "common", "cfg",
+				"lane0", "lane1", "lane2";
+	};
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 5547984..d5e2b50f 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -446,6 +446,14 @@ config PHY_STIH41X_USB
 	  Enable this to support the USB transceiver that is part of
 	  STMicroelectronics STiH41x SoC series.
 
+config PHY_QCOM_QMP
+	tristate "Qualcomm QMP PHY Driver"
+	depends on OF && (ARCH_QCOM || COMPILE_TEST)
+	select GENERIC_PHY
+	help
+	  Enable this to support the QMP PHY transceiver that is used
+	  with controllers such as PCIe, UFS, and USB on Qualcomm chips.
+
 config PHY_QCOM_QUSB2
 	tristate "Qualcomm QUSB2 PHY Driver"
 	depends on OF && (ARCH_QCOM || COMPILE_TEST)
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 848489d..fde9fba 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+= phy-spear1340-miphy.o
 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_QMP) 	+= phy-qcom-qmp.o
 obj-$(CONFIG_PHY_QCOM_QUSB2) 	+= phy-qcom-qusb2.o
 obj-$(CONFIG_PHY_QCOM_UFS) 	+= phy-qcom-ufs.o
 obj-$(CONFIG_PHY_QCOM_UFS) 	+= phy-qcom-ufs-qmp-20nm.o
diff --git a/drivers/phy/phy-qcom-qmp.c b/drivers/phy/phy-qcom-qmp.c
new file mode 100644
index 0000000..7e89179
--- /dev/null
+++ b/drivers/phy/phy-qcom-qmp.c
@@ -0,0 +1,1154 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. 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/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/phy/phy.h>
+
+/* QMP PHY QSERDES COM registers */
+#define QSERDES_COM_BG_TIMER				0x00c
+#define QSERDES_COM_SSC_EN_CENTER			0x010
+#define QSERDES_COM_SSC_ADJ_PER1			0x014
+#define QSERDES_COM_SSC_ADJ_PER2			0x018
+#define QSERDES_COM_SSC_PER1				0x01c
+#define QSERDES_COM_SSC_PER2				0x020
+#define QSERDES_COM_SSC_STEP_SIZE1			0x024
+#define QSERDES_COM_SSC_STEP_SIZE2			0x028
+#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN			0x034
+#define QSERDES_COM_CLK_ENABLE1				0x038
+#define QSERDES_COM_SYS_CLK_CTRL			0x03c
+#define QSERDES_COM_SYSCLK_BUF_ENABLE			0x040
+#define QSERDES_COM_PLL_IVCO				0x048
+#define QSERDES_COM_LOCK_CMP1_MODE0			0x04c
+#define QSERDES_COM_LOCK_CMP2_MODE0			0x050
+#define QSERDES_COM_LOCK_CMP3_MODE0			0x054
+#define QSERDES_COM_LOCK_CMP1_MODE1			0x058
+#define QSERDES_COM_LOCK_CMP2_MODE1			0x05c
+#define QSERDES_COM_LOCK_CMP3_MODE1			0x060
+#define QSERDES_COM_BG_TRIM				0x070
+#define QSERDES_COM_CLK_EP_DIV				0x074
+#define QSERDES_COM_CP_CTRL_MODE0			0x078
+#define QSERDES_COM_CP_CTRL_MODE1			0x07c
+#define QSERDES_COM_PLL_RCTRL_MODE0			0x084
+#define QSERDES_COM_PLL_RCTRL_MODE1			0x088
+#define QSERDES_COM_PLL_CCTRL_MODE0			0x090
+#define QSERDES_COM_PLL_CCTRL_MODE1			0x094
+#define QSERDES_COM_SYSCLK_EN_SEL			0x0ac
+#define QSERDES_COM_RESETSM_CNTRL			0x0b4
+#define QSERDES_COM_RESTRIM_CTRL			0x0bc
+#define QSERDES_COM_RESCODE_DIV_NUM			0x0c4
+#define QSERDES_COM_LOCK_CMP_EN				0x0c8
+#define QSERDES_COM_LOCK_CMP_CFG			0x0cc
+#define QSERDES_COM_DEC_START_MODE0			0x0d0
+#define QSERDES_COM_DEC_START_MODE1			0x0d4
+#define QSERDES_COM_DIV_FRAC_START1_MODE0		0x0dc
+#define QSERDES_COM_DIV_FRAC_START2_MODE0		0x0e0
+#define QSERDES_COM_DIV_FRAC_START3_MODE0		0x0e4
+#define QSERDES_COM_DIV_FRAC_START1_MODE1		0x0e8
+#define QSERDES_COM_DIV_FRAC_START2_MODE1		0x0ec
+#define QSERDES_COM_DIV_FRAC_START3_MODE1		0x0f0
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0		0x108
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0		0x10c
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1		0x110
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1		0x114
+#define QSERDES_COM_VCO_TUNE_CTRL			0x124
+#define QSERDES_COM_VCO_TUNE_MAP			0x128
+#define QSERDES_COM_VCO_TUNE1_MODE0			0x12c
+#define QSERDES_COM_VCO_TUNE2_MODE0			0x130
+#define QSERDES_COM_VCO_TUNE1_MODE1			0x134
+#define QSERDES_COM_VCO_TUNE2_MODE1			0x138
+#define QSERDES_COM_VCO_TUNE_TIMER1			0x144
+#define QSERDES_COM_VCO_TUNE_TIMER2			0x148
+#define QSERDES_COM_BG_CTRL				0x170
+#define QSERDES_COM_CLK_SELECT				0x174
+#define QSERDES_COM_HSCLK_SEL				0x178
+#define QSERDES_COM_CORECLK_DIV				0x184
+#define QSERDES_COM_CORE_CLK_EN				0x18c
+#define QSERDES_COM_C_READY_STATUS			0x190
+#define QSERDES_COM_CMN_CONFIG				0x194
+#define QSERDES_COM_SVS_MODE_CLK_SEL			0x19c
+#define QSERDES_COM_DEBUG_BUS0				0x1a0
+#define QSERDES_COM_DEBUG_BUS1				0x1a4
+#define QSERDES_COM_DEBUG_BUS2				0x1a8
+#define QSERDES_COM_DEBUG_BUS3				0x1ac
+#define QSERDES_COM_DEBUG_BUS_SEL			0x1b0
+#define QSERDES_COM_CORECLK_DIV_MODE1			0x1bc
+
+/* QMP PHY TX registers */
+#define QSERDES_TX_RES_CODE_LANE_OFFSET			0x054
+#define QSERDES_TX_DEBUG_BUS_SEL			0x064
+#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN	0x068
+#define QSERDES_TX_LANE_MODE				0x094
+#define QSERDES_TX_RCV_DETECT_LVL_2			0x0ac
+
+/* QMP PHY RX registers */
+#define QSERDES_RX_UCDR_SO_GAIN_HALF			0x010
+#define QSERDES_RX_UCDR_SO_GAIN				0x01c
+#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN		0x040
+#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE	0x048
+#define QSERDES_RX_RX_TERM_BW				0x090
+#define QSERDES_RX_RX_EQ_GAIN1_LSB			0x0c4
+#define QSERDES_RX_RX_EQ_GAIN1_MSB			0x0c8
+#define QSERDES_RX_RX_EQ_GAIN2_LSB			0x0cc
+#define QSERDES_RX_RX_EQ_GAIN2_MSB			0x0d0
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2		0x0d8
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3		0x0dc
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4		0x0e0
+#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1		0x108
+#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2		0x10c
+#define QSERDES_RX_SIGDET_ENABLES			0x110
+#define QSERDES_RX_SIGDET_CNTRL				0x114
+#define QSERDES_RX_SIGDET_LVL				0x118
+#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL		0x11c
+#define QSERDES_RX_RX_BAND				0x120
+#define QSERDES_RX_RX_INTERFACE_MODE			0x12c
+
+/* QMP PHY PCS registers */
+#define QPHY_SW_RESET					0x00
+#define QPHY_POWER_DOWN_CONTROL				0x04
+#define QPHY_START_CTRL					0x08
+#define QPHY_TXDEEMPH_M6DB_V0				0x24
+#define QPHY_TXDEEMPH_M3P5DB_V0				0x28
+#define QPHY_ENDPOINT_REFCLK_DRIVE			0x54
+#define QPHY_RX_IDLE_DTCT_CNTRL				0x58
+#define QPHY_POWER_STATE_CONFIG1			0x60
+#define QPHY_POWER_STATE_CONFIG2			0x64
+#define QPHY_POWER_STATE_CONFIG4			0x6c
+#define QPHY_LOCK_DETECT_CONFIG1			0x80
+#define QPHY_LOCK_DETECT_CONFIG2			0x84
+#define QPHY_LOCK_DETECT_CONFIG3			0x88
+#define QPHY_PWRUP_RESET_DLY_TIME_AUXCLK		0xa0
+#define QPHY_LP_WAKEUP_DLY_TIME_AUXCLK			0xa4
+
+/* PHY_SW_RESET bit */
+#define PHY_SW_RESET				BIT(0)
+/* PHY_POWER_DOWN_CONTROL */
+#define PHY_SW_PWRDN				BIT(0)
+#define PHY_REFCLK_DRV_DSBL			BIT(1)
+/* PHY_START_CONTROL bits */
+#define PHY_SERDES_START			BIT(0)
+#define PHY_PCS_START				BIT(1)
+#define PHY_PLL_READY_GATE_EN			BIT(3)
+/* PHY_PCS_STATUS bit */
+#define MASK_PHYSTATUS				BIT(6)
+/* PCS_READY_STATUS bit */
+#define MASK_COM_PCS_READY			BIT(0)
+
+#define REFCLK_STABILIZATION_DELAY_US_MIN	1000
+#define REFCLK_STABILIZATION_DELAY_US_MAX	1005
+#define PHY_READY_TIMEOUT_COUNT			10
+#define POWER_DOWN_DELAY_US_MIN			10
+#define POWER_DOWN_DELAY_US_MAX			11
+
+#define MAX_PROP_NAME		32
+
+struct qmp_phy_init_tbl {
+	unsigned int reg_offset;
+	unsigned int cfg_val;
+	/*
+	 * register part of layout ?
+	 * if yes, then reg_offset gives index in the reg-layout
+	 */
+	int in_layout;
+};
+#define QCOM_QMP_PHY_INIT_CFG(reg, val) \
+	{				\
+		.reg_offset = reg,	\
+		.cfg_val = val,		\
+	}
+#define QCOM_QMP_PHY_INIT_CFG_L(reg, val) \
+	{				  \
+		.reg_offset = reg,	  \
+		.cfg_val = val,		  \
+		.in_layout = 1,		  \
+	}
+
+/* set of registers with offsets different per-PHY */
+enum qphy_reg_layout {
+	/* Common block control registers */
+	QPHY_COM_SW_RESET,
+	QPHY_COM_POWER_DOWN_CONTROL,
+	QPHY_COM_START_CONTROL,
+	QPHY_COM_PCS_READY_STATUS,
+	/* PCS registers */
+	QPHY_PLL_LOCK_CHK_DLY_TIME,
+	QPHY_FLL_CNTRL1,
+	QPHY_FLL_CNTRL2,
+	QPHY_FLL_CNT_VAL_L,
+	QPHY_FLL_CNT_VAL_H_TOL,
+	QPHY_FLL_MAN_CODE,
+	QPHY_PCS_READY_STATUS,
+};
+
+unsigned int pciephy_regs_layout[] = {
+	[QPHY_COM_SW_RESET]		= 0x400,
+	[QPHY_COM_POWER_DOWN_CONTROL]	= 0x404,
+	[QPHY_COM_START_CONTROL]	= 0x408,
+	[QPHY_COM_PCS_READY_STATUS]	= 0x448,
+	[QPHY_PLL_LOCK_CHK_DLY_TIME]	= 0xa8,
+	[QPHY_FLL_CNTRL1]		= 0xc4,
+	[QPHY_FLL_CNTRL2]		= 0xc8,
+	[QPHY_FLL_CNT_VAL_L]		= 0xcc,
+	[QPHY_FLL_CNT_VAL_H_TOL]	= 0xd0,
+	[QPHY_FLL_MAN_CODE]		= 0xd4,
+	[QPHY_PCS_READY_STATUS]		= 0x174,
+};
+
+unsigned int usb3phy_regs_layout[] = {
+	[QPHY_FLL_CNTRL1]		= 0xc0,
+	[QPHY_FLL_CNTRL2]		= 0xc4,
+	[QPHY_FLL_CNT_VAL_L]		= 0xc8,
+	[QPHY_FLL_CNT_VAL_H_TOL]	= 0xcc,
+	[QPHY_FLL_MAN_CODE]		= 0xd0,
+	[QPHY_PCS_READY_STATUS]		= 0x17c,
+};
+
+static struct qmp_phy_init_tbl pciephy_serdes_init_tbl[] = {
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1c),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x42),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x1f),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x01),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x09),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x1a),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x0a),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x02),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1f),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x04),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x02),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x2f),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x19),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x15),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_EP_DIV, 0x19),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x40),
+};
+
+static struct qmp_phy_init_tbl pciephy_tx_init_tbl[] = {
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06),
+};
+
+static struct qmp_phy_init_tbl pciephy_rx_init_tbl[] = {
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x1c),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x01),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xdb),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_BAND, 0x18),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN_HALF, 0x04),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x14),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x19),
+};
+
+static struct qmp_phy_init_tbl pciephy_pcs_init_tbl[] = {
+	QCOM_QMP_PHY_INIT_CFG(QPHY_RX_IDLE_DTCT_CNTRL, 0x4c),
+	QCOM_QMP_PHY_INIT_CFG(QPHY_PWRUP_RESET_DLY_TIME_AUXCLK, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QPHY_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01),
+
+	QCOM_QMP_PHY_INIT_CFG_L(QPHY_PLL_LOCK_CHK_DLY_TIME, 0x05),
+
+	QCOM_QMP_PHY_INIT_CFG(QPHY_ENDPOINT_REFCLK_DRIVE, 0x05),
+	QCOM_QMP_PHY_INIT_CFG(QPHY_POWER_DOWN_CONTROL, 0x02),
+	QCOM_QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG4, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG1, 0xa3),
+	QCOM_QMP_PHY_INIT_CFG(QPHY_TXDEEMPH_M3P5DB_V0, 0x0e),
+};
+
+static struct qmp_phy_init_tbl usb3phy_serdes_init_tbl[] = {
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x04),
+	/* PLL and Loop filter settings */
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x15),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x34),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a),
+	/* SSC settings */
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0xde),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x07),
+};
+
+static struct qmp_phy_init_tbl usb3phy_tx_init_tbl[] = {
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_TX_RCV_DETECT_LVL_2, 0x12),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06),
+};
+
+static struct qmp_phy_init_tbl usb3phy_rx_init_tbl[] = {
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xbb),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x03),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x18),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x16),
+};
+
+static struct qmp_phy_init_tbl usb3phy_pcs_init_tbl[] = {
+	/* FLL settings */
+	QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_CNTRL2, 0x03),
+	QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_CNTRL1, 0x02),
+	QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_CNT_VAL_L, 0x09),
+	QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_CNT_VAL_H_TOL, 0x42),
+	QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_MAN_CODE, 0x85),
+
+	/* Lock Det settings */
+	QCOM_QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG1, 0xd1),
+	QCOM_QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG2, 0x1f),
+	QCOM_QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG3, 0x47),
+	QCOM_QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG2, 0x08),
+};
+
+/**
+ * struct qmp_phy_init_cfg:- per-PHY init config.
+ */
+struct qmp_phy_init_cfg {
+	/*
+	 * @type: phy-type - PCIE/UFS/USB
+	 * @nlanes: number of lanes provided by phy
+	 * @tx_offsets: array of offsets of Tx block from PHY base
+	 * @rx_offsets: array of offsets of Rx block from PHY base
+	 * @pcs_offsets: array of offsets of PCS block from PHY base
+	 */
+	unsigned int type;
+	int nlanes;
+	unsigned int *tx_offsets;
+	unsigned int *rx_offsets;
+	unsigned int *pcs_offsets;
+
+	/* Initialization sequence for PHY blocks - Serdes, tx, rx, pcs */
+	struct qmp_phy_init_tbl *phy_init_serdes_tbl;
+	int phy_init_serdes_tbl_sz;
+	struct qmp_phy_init_tbl *phy_init_tx_tbl;
+	int phy_init_tx_tbl_sz;
+	struct qmp_phy_init_tbl *phy_init_rx_tbl;
+	int phy_init_rx_tbl_sz;
+	struct qmp_phy_init_tbl *phy_init_pcs_tbl;
+	int phy_init_pcs_tbl_sz;
+
+	/* array of registers with different offsets */
+	unsigned int *regs;
+
+	unsigned int mask_start_ctrl;
+	unsigned int mask_pwr_dn_ctrl;
+	/* true, if PHY has a separate PHY_COM_CNTRL block */
+	bool has_phy_com_ctrl;
+};
+
+/**
+ * struct qmp_phy_desc:- per-lane phy-descriptor.
+ *
+ * @phy: pointer to generic phy
+ * @tx: pointer to iomapped memory space for PHY's tx
+ * @rx: pointer to iomapped memory space for PHY's rx
+ * @pcs: pointer to iomapped memory space for PHY's pcs
+ * @pipe_clk: pointer to pipe lock
+ * @index: lane index
+ * @qphy: pointer to QMP phy to which this lane belongs
+ * @lane_rst: pointer to lane's reset controller
+ */
+struct qmp_phy_desc {
+	struct phy *phy;
+	void __iomem *tx;
+	void __iomem *rx;
+	void __iomem *pcs;
+	struct clk *pipe_clk;
+	unsigned int index;
+	struct qcom_qmp_phy *qphy;
+	struct reset_control *lane_rst;
+};
+
+/**
+ * struct qcom_qmp_phy:- structure holding QMP PHY attributes.
+ *
+ * @dev: pointer to device
+ * @serdes: pointer to iomapped memory space for phy's serdes
+ *
+ * @aux_clk: pointer to phy core clock
+ * @cfg_ahb_clk: pointer to AHB2PHY interface clock
+ * @ref_clk: pointer to reference clock
+ * @ref_clk_src: pointer to source to reference clock
+ * @clk_enabled: check if clocks are enabled or not
+ *
+ * @vdda_phy: vdd supply to the phy core block
+ * @vdda_pll: 1.8V vdd supply to ref_clk block
+ * @vddp_ref_clk: vdd supply to specific ref_clk block
+ *
+ * @phy_rst: Pointer to phy reset control
+ * @phycom_rst: Pointer to phy common reset control
+ * @phycfg_rst: Pointer to phy ahb cfg reset control (Optional)
+ *
+ * @cfg: pointer to init config for each phys
+ * @phys: array of pointer to per-lane phy descriptors
+ * @phy_mutex: mutex lock for PHY common block initialization
+ * @init_count: Phy common block initialization count
+ */
+struct qcom_qmp_phy {
+	struct device *dev;
+	void __iomem *serdes;
+
+	struct clk *aux_clk;
+	struct clk *cfg_ahb_clk;
+	struct clk *ref_clk;
+	struct clk *ref_clk_src;
+	bool clk_enabled;
+
+	struct regulator *vdda_phy;
+	struct regulator *vdda_pll;
+	struct regulator *vddp_ref_clk;
+
+	struct reset_control *phy_rst;
+	struct reset_control *phycom_rst;
+	struct reset_control *phycfg_rst;
+
+	const struct qmp_phy_init_cfg *cfg;
+	struct qmp_phy_desc **phys;
+
+	struct mutex phy_mutex;
+	int init_count;
+};
+
+static inline void qphy_setbits(u32 bitmask, void __iomem *reg)
+{
+	u32 val;
+
+	val = readl_relaxed(reg);
+	val |= bitmask;
+	writel_relaxed(val, reg);
+}
+
+static inline void qphy_clrbits(u32 bitmask, void __iomem *reg)
+{
+	u32 val;
+
+	val = readl_relaxed(reg);
+	val &= ~bitmask;
+	writel_relaxed(val, reg);
+}
+
+unsigned int msm8996_pciephy_tx_offsets[] = { 0x1000, 0x2000, 0x3000 };
+unsigned int msm8996_pciephy_rx_offsets[] = { 0x1200, 0x2200, 0x3200 };
+unsigned int msm8996_pciephy_pcs_offsets[] = { 0x1400, 0x2400, 0x3400 };
+
+unsigned int msm8996_usb3phy_tx_offsets[] = { 0x200 };
+unsigned int msm8996_usb3phy_rx_offsets[] = { 0x400 };
+unsigned int msm8996_usb3phy_pcs_offsets[] = { 0x600 };
+
+const struct qmp_phy_init_cfg pciephy_init_cfg = {
+	.type			= PHY_TYPE_PCIE,
+	.nlanes			= 3,
+	.tx_offsets		= msm8996_pciephy_tx_offsets,
+	.rx_offsets		= msm8996_pciephy_rx_offsets,
+	.pcs_offsets		= msm8996_pciephy_pcs_offsets,
+
+	.phy_init_serdes_tbl	= pciephy_serdes_init_tbl,
+	.phy_init_serdes_tbl_sz	= ARRAY_SIZE(pciephy_serdes_init_tbl),
+	.phy_init_tx_tbl	= pciephy_tx_init_tbl,
+	.phy_init_tx_tbl_sz	= ARRAY_SIZE(pciephy_tx_init_tbl),
+	.phy_init_rx_tbl	= pciephy_rx_init_tbl,
+	.phy_init_rx_tbl_sz	= ARRAY_SIZE(pciephy_rx_init_tbl),
+	.phy_init_pcs_tbl	= pciephy_pcs_init_tbl,
+	.phy_init_pcs_tbl_sz	= ARRAY_SIZE(pciephy_pcs_init_tbl),
+	.has_phy_com_ctrl	= true,
+	.regs			= pciephy_regs_layout,
+	.mask_start_ctrl	= (PHY_PCS_START | PHY_PLL_READY_GATE_EN),
+	.mask_pwr_dn_ctrl	= (PHY_SW_PWRDN | PHY_REFCLK_DRV_DSBL),
+};
+
+const struct qmp_phy_init_cfg usb3phy_init_cfg = {
+	.type			= PHY_TYPE_USB3,
+	.nlanes			= 1,
+	.tx_offsets		= msm8996_usb3phy_tx_offsets,
+	.rx_offsets		= msm8996_usb3phy_rx_offsets,
+	.pcs_offsets		= msm8996_usb3phy_pcs_offsets,
+
+	.phy_init_serdes_tbl	= usb3phy_serdes_init_tbl,
+	.phy_init_serdes_tbl_sz	= ARRAY_SIZE(usb3phy_serdes_init_tbl),
+	.phy_init_tx_tbl	= usb3phy_tx_init_tbl,
+	.phy_init_tx_tbl_sz	= ARRAY_SIZE(usb3phy_tx_init_tbl),
+	.phy_init_rx_tbl	= usb3phy_rx_init_tbl,
+	.phy_init_rx_tbl_sz	= ARRAY_SIZE(usb3phy_rx_init_tbl),
+	.phy_init_pcs_tbl	= usb3phy_pcs_init_tbl,
+	.phy_init_pcs_tbl_sz	= ARRAY_SIZE(usb3phy_pcs_init_tbl),
+	.regs			= usb3phy_regs_layout,
+	.mask_start_ctrl	= (PHY_SERDES_START | PHY_PCS_START),
+	.mask_pwr_dn_ctrl	= PHY_SW_PWRDN,
+};
+
+static void qcom_qmp_phy_configure(void __iomem *base,
+				unsigned int *regs_layout,
+				struct qmp_phy_init_tbl init_tbl[],
+				int init_tbl_sz)
+{
+	int i;
+
+	for (i = 0; i < init_tbl_sz; i++) {
+		if (init_tbl[i].in_layout)
+			writel_relaxed(init_tbl[i].cfg_val,
+				base + regs_layout[init_tbl[i].reg_offset]);
+		else
+			writel_relaxed(init_tbl[i].cfg_val,
+				base + init_tbl[i].reg_offset);
+	}
+
+	/* flush buffered writes */
+	mb();
+}
+
+static int qcom_qmp_phy_poweron(struct phy *phy)
+{
+	struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
+	struct qcom_qmp_phy *qphy = phydesc->qphy;
+	int ret;
+
+	dev_info(&phy->dev, "Powering on QMP phy\n");
+
+	ret = regulator_enable(qphy->vdda_phy);
+	if (ret) {
+		dev_err(qphy->dev, "%s: vdda-phy enable failed, err=%d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	ret = regulator_enable(qphy->vdda_pll);
+	if (ret) {
+		dev_err(qphy->dev, "%s: vdda-pll enable failed, err=%d\n",
+				__func__, ret);
+		goto err_vdda_pll;
+	}
+
+	if (qphy->vddp_ref_clk) {
+		ret = regulator_enable(qphy->vddp_ref_clk);
+		if (ret) {
+			dev_err(qphy->dev, "%s: vdda-ref-clk enable failed, err=%d\n",
+					__func__, ret);
+			goto err_vddp_refclk;
+		}
+	}
+
+	if (!qphy->clk_enabled) {
+		clk_prepare_enable(qphy->ref_clk_src);
+		clk_prepare_enable(qphy->ref_clk);
+		clk_prepare_enable(phydesc->pipe_clk);
+		qphy->clk_enabled = true;
+	}
+
+	return 0;
+
+err_vddp_refclk:
+	regulator_disable(qphy->vdda_pll);
+err_vdda_pll:
+	regulator_disable(qphy->vdda_phy);
+	return ret;
+}
+
+static int qcom_qmp_phy_poweroff(struct phy *phy)
+{
+	struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
+	struct qcom_qmp_phy *qphy = phydesc->qphy;
+
+	if (qphy->clk_enabled) {
+		clk_disable_unprepare(qphy->ref_clk_src);
+		clk_disable_unprepare(qphy->ref_clk);
+		clk_disable_unprepare(phydesc->pipe_clk);
+		qphy->clk_enabled = false;
+	}
+
+	if (qphy->vddp_ref_clk)
+		regulator_disable(qphy->vddp_ref_clk);
+
+	regulator_disable(qphy->vdda_pll);
+	regulator_disable(qphy->vdda_phy);
+
+	return 0;
+}
+
+static int qcom_qmp_phy_is_ready(struct qcom_qmp_phy *qphy,
+				void __iomem *pcs_status, u32 mask)
+{
+	unsigned int init_timeout;
+
+	init_timeout = PHY_READY_TIMEOUT_COUNT;
+	do {
+		if (readl_relaxed(pcs_status) & mask)
+			break;
+
+		usleep_range(REFCLK_STABILIZATION_DELAY_US_MIN,
+				 REFCLK_STABILIZATION_DELAY_US_MAX);
+	} while (--init_timeout);
+
+	if (!init_timeout)
+		return -EBUSY;
+
+	return 0;
+}
+
+static int qcom_qmp_phy_com_init(struct qcom_qmp_phy *qphy)
+{
+	const struct qmp_phy_init_cfg *cfg = qphy->cfg;
+	void __iomem *serdes = qphy->serdes;
+	int ret;
+
+	mutex_lock(&qphy->phy_mutex);
+	if (qphy->init_count++) {
+		mutex_unlock(&qphy->phy_mutex);
+		return 0;
+	}
+
+	ret = reset_control_deassert(qphy->phy_rst);
+	if (ret) {
+		dev_err(qphy->dev, "phy reset deassert failed\n");
+		return ret;
+	}
+
+	ret = reset_control_deassert(qphy->phycom_rst);
+	if (ret) {
+		dev_err(qphy->dev, "common reset deassert failed\n");
+		goto err_phycom_rst;
+	}
+
+	if (qphy->phycfg_rst) {
+		ret = reset_control_deassert(qphy->phycfg_rst);
+		if (ret) {
+			dev_err(qphy->dev, "common reset deassert failed\n");
+			goto err_phycfg_rst;
+		}
+	}
+
+	if (cfg->has_phy_com_ctrl) {
+		qphy_setbits(PHY_SW_PWRDN,
+			serdes + cfg->regs[QPHY_COM_POWER_DOWN_CONTROL]);
+		/* Make sure that above write is completed */
+		mb();
+	}
+
+	/* Serdes configuration */
+	qcom_qmp_phy_configure(serdes, cfg->regs, cfg->phy_init_serdes_tbl,
+				cfg->phy_init_serdes_tbl_sz);
+
+	if (cfg->has_phy_com_ctrl) {
+		qphy_clrbits(PHY_SW_RESET, serdes +
+					cfg->regs[QPHY_COM_SW_RESET]);
+		qphy_setbits(PHY_SERDES_START | PHY_PCS_START,
+				serdes + cfg->regs[QPHY_COM_START_CONTROL]);
+		/* Make sure that above write is completed */
+		mb();
+
+		ret = qcom_qmp_phy_is_ready(qphy, serdes +
+					cfg->regs[QPHY_COM_PCS_READY_STATUS],
+					MASK_COM_PCS_READY);
+		if (ret) {
+			dev_err(qphy->dev,
+				"common control block init timed-out\n");
+			goto err_phy_comctrl;
+		}
+	}
+
+	mutex_unlock(&qphy->phy_mutex);
+
+	return 0;
+
+err_phy_comctrl:
+	if (qphy->phycfg_rst)
+		reset_control_assert(qphy->phycfg_rst);
+err_phycfg_rst:
+	reset_control_assert(qphy->phycom_rst);
+err_phycom_rst:
+	reset_control_assert(qphy->phy_rst);
+	return ret;
+}
+
+static int qcom_qmp_phy_com_exit(struct qcom_qmp_phy *qphy)
+{
+	const struct qmp_phy_init_cfg *cfg = qphy->cfg;
+	void __iomem *serdes = qphy->serdes;
+
+	mutex_lock(&qphy->phy_mutex);
+	if (--qphy->init_count) {
+		mutex_unlock(&qphy->phy_mutex);
+		return 0;
+	}
+
+	if (cfg->has_phy_com_ctrl) {
+		qphy_setbits(PHY_SERDES_START | PHY_PCS_START,
+				serdes + cfg->regs[QPHY_COM_START_CONTROL]);
+		qphy_clrbits(PHY_SW_RESET, serdes +
+					cfg->regs[QPHY_COM_SW_RESET]);
+		qphy_setbits(PHY_SW_PWRDN,
+			serdes + cfg->regs[QPHY_COM_POWER_DOWN_CONTROL]);
+
+		/* Make sure that above writes are completed */
+		mb();
+	}
+
+	reset_control_assert(qphy->phy_rst);
+	reset_control_assert(qphy->phycom_rst);
+	if (qphy->phycfg_rst)
+		reset_control_assert(qphy->phycfg_rst);
+
+	mutex_unlock(&qphy->phy_mutex);
+
+	return 0;
+}
+
+/* PHY Initialization */
+static int qcom_qmp_phy_init(struct phy *phy)
+{
+	struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
+	struct qcom_qmp_phy *qphy = phydesc->qphy;
+	const struct qmp_phy_init_cfg *cfg = qphy->cfg;
+	void __iomem *tx = phydesc->tx;
+	void __iomem *rx = phydesc->rx;
+	void __iomem *pcs = phydesc->pcs;
+	int ret;
+
+	dev_info(qphy->dev, "Initializing QMP phy\n");
+
+	/* enable interface clocks to program phy */
+	clk_prepare_enable(qphy->aux_clk);
+	clk_prepare_enable(qphy->cfg_ahb_clk);
+
+	ret = qcom_qmp_phy_com_init(qphy);
+	if (ret)
+		goto err;
+
+	if (phydesc->lane_rst) {
+		ret = reset_control_deassert(phydesc->lane_rst);
+		if (ret) {
+			dev_err(qphy->dev, "lane<%d> reset deassert failed\n",
+					phydesc->index);
+			goto err_lane_rst;
+		}
+	}
+
+	/* Tx, Rx, and PCS configurations */
+	qcom_qmp_phy_configure(tx, cfg->regs, cfg->phy_init_tx_tbl,
+				cfg->phy_init_tx_tbl_sz);
+	qcom_qmp_phy_configure(rx, cfg->regs, cfg->phy_init_rx_tbl,
+				cfg->phy_init_rx_tbl_sz);
+	qcom_qmp_phy_configure(pcs, cfg->regs, cfg->phy_init_pcs_tbl,
+				cfg->phy_init_pcs_tbl_sz);
+
+	/*
+	 * Pull out PHY from POWER DOWN state:
+	 * This is active low enable signal to power-down PHY.
+	 */
+	qphy_setbits(cfg->mask_pwr_dn_ctrl,
+			pcs + QPHY_POWER_DOWN_CONTROL);
+	/* XXX: 10 us delay; given in PCIE HPG only */
+	usleep_range(POWER_DOWN_DELAY_US_MIN, POWER_DOWN_DELAY_US_MAX);
+
+	/* start SerDes and Phy-Coding-Sublayer */
+	qphy_setbits(cfg->mask_start_ctrl, pcs + QPHY_START_CTRL);
+
+	/* Pull PHY out of reset state */
+	qphy_clrbits(PHY_SW_RESET, pcs + QPHY_SW_RESET);
+	/* Make sure that above writes are completed */
+	mb();
+
+	ret = qcom_qmp_phy_is_ready(qphy, pcs +
+					cfg->regs[QPHY_PCS_READY_STATUS],
+					MASK_PHYSTATUS);
+	if (ret) {
+		dev_err(qphy->dev, "phy initialization timed-out\n");
+		goto err_pcs_ready;
+	}
+
+	return 0;
+
+err_pcs_ready:
+	if (phydesc->lane_rst)
+		reset_control_assert(phydesc->lane_rst);
+err_lane_rst:
+	qcom_qmp_phy_com_exit(qphy);
+err:
+	clk_disable_unprepare(qphy->cfg_ahb_clk);
+	clk_disable_unprepare(qphy->aux_clk);
+	return ret;
+}
+
+static int qcom_qmp_phy_exit(struct phy *phy)
+{
+	struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
+	struct qcom_qmp_phy *qphy = phydesc->qphy;
+	const struct qmp_phy_init_cfg *cfg = qphy->cfg;
+
+	/* PHY reset */
+	qphy_setbits(PHY_SW_RESET, phydesc->pcs + QPHY_SW_RESET);
+
+	/* stop SerDes and Phy-Coding-Sublayer */
+	qphy_clrbits(cfg->mask_start_ctrl, phydesc->pcs + QPHY_START_CTRL);
+
+	/* Put PHY into POWER DOWN state: active low */
+	qphy_clrbits(cfg->mask_pwr_dn_ctrl,
+			phydesc->pcs + QPHY_POWER_DOWN_CONTROL);
+
+	/* Make sure that above writes are completed */
+	mb();
+
+	if (phydesc->lane_rst)
+		reset_control_assert(phydesc->lane_rst);
+
+	qcom_qmp_phy_com_exit(qphy);
+
+	clk_disable_unprepare(qphy->aux_clk);
+	clk_disable_unprepare(qphy->cfg_ahb_clk);
+
+	return 0;
+}
+
+
+static int qcom_qmp_phy_regulator_init(struct device *dev)
+{
+	struct qcom_qmp_phy *qphy = dev_get_drvdata(dev);
+	int ret = 0;
+
+	qphy->vdda_phy = devm_regulator_get(dev, "vdda-phy");
+	if (IS_ERR(qphy->vdda_phy)) {
+		ret = PTR_ERR(qphy->vdda_phy);
+		dev_err(dev, "failed to get vdda-phy, %d\n", ret);
+		return ret;
+	}
+
+	qphy->vdda_pll = devm_regulator_get(dev, "vdda-pll");
+	if (IS_ERR(qphy->vdda_pll)) {
+		ret = PTR_ERR(qphy->vdda_pll);
+		dev_err(dev, "failed to get vdda-pll, %d\n", ret);
+		return ret;
+	}
+
+	/* optional regulator */
+	qphy->vddp_ref_clk = devm_regulator_get(dev, "vddp-ref-clk");
+	if (IS_ERR(qphy->vddp_ref_clk)) {
+		ret = PTR_ERR(qphy->vddp_ref_clk);
+		dev_info(dev, "failed to get vddp-ref-clk, %d\n", ret);
+		qphy->vddp_ref_clk = NULL;
+	}
+
+	return 0;
+}
+
+static int qcom_qmp_phy_clk_init(struct device *dev)
+{
+	struct qcom_qmp_phy *qphy = dev_get_drvdata(dev);
+	int ret;
+
+	qphy->aux_clk = devm_clk_get(dev, "aux");
+	if (IS_ERR(qphy->aux_clk)) {
+		ret = PTR_ERR(qphy->aux_clk);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "failed to get aux_clk\n");
+		return ret;
+	}
+
+	qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb");
+	if (IS_ERR(qphy->cfg_ahb_clk)) {
+		ret = PTR_ERR(qphy->cfg_ahb_clk);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "failed to get cfg_ahb_clk\n");
+		return ret;
+	}
+
+	/*
+	 * ref_clk and ref_clk_src handles may not be available in
+	 * all hardwares. So we don't return error in these cases.
+	 */
+	qphy->ref_clk_src = devm_clk_get(dev, "ref_clk_src");
+	if (IS_ERR(qphy->ref_clk_src)) {
+		ret = PTR_ERR(qphy->ref_clk_src);
+		if (ret != -EPROBE_DEFER) {
+			qphy->ref_clk_src = NULL;
+			dev_dbg(dev, "failed to get ref_clk_src\n");
+		} else {
+			return ret;
+		}
+	}
+
+	qphy->ref_clk = devm_clk_get(dev, "ref_clk");
+	if (IS_ERR(qphy->ref_clk)) {
+		ret = PTR_ERR(qphy->ref_clk);
+		if (ret != -EPROBE_DEFER) {
+			qphy->ref_clk = NULL;
+			dev_dbg(dev, "failed to get ref_clk\n");
+		} else {
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static struct phy *qcom_qmp_phy_xlate(struct device *dev,
+					struct of_phandle_args *args)
+{
+	struct qcom_qmp_phy *qphy = dev_get_drvdata(dev);
+	int i;
+
+	if (WARN_ON(args->args[0] >= qphy->cfg->nlanes))
+		return ERR_PTR(-ENODEV);
+
+	for (i = 0; i < qphy->cfg->nlanes; i++) {
+		if (qphy->phys[i]->index == args->args[0])
+			break;
+	}
+
+	if (i == qphy->cfg->nlanes)
+		return ERR_PTR(-ENODEV);
+
+	return qphy->phys[i]->phy;
+}
+
+static const struct phy_ops qcom_qmp_phy_gen_ops = {
+	.init		= qcom_qmp_phy_init,
+	.exit		= qcom_qmp_phy_exit,
+	.power_on	= qcom_qmp_phy_poweron,
+	.power_off	= qcom_qmp_phy_poweroff,
+	.owner		= THIS_MODULE,
+};
+
+static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
+	{
+		.compatible = "qcom,msm8996-qmp-pcie-phy",
+		.data = &pciephy_init_cfg,
+	}, {
+		.compatible = "qcom,msm8996-qmp-usb3-phy",
+		.data = &usb3phy_init_cfg,
+	},
+	{ },
+};
+MODULE_DEVICE_TABLE(of, qcom_qmp_phy_of_match_table);
+
+static int qcom_qmp_phy_probe(struct platform_device *pdev)
+{
+	struct qcom_qmp_phy *qphy;
+	struct device *dev = &pdev->dev;
+	struct device_node *child;
+	struct phy_provider *phy_provider;
+	struct resource *res;
+	const struct of_device_id *match;
+	void __iomem *base;
+	int ret = 0;
+	int id;
+
+	qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
+	if (!qphy)
+		return -ENOMEM;
+	qphy->dev = dev;
+	dev_set_drvdata(dev, qphy);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+	base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	/* per PHY serdes; usually located at base address */
+	qphy->serdes = base;
+
+	mutex_init(&qphy->phy_mutex);
+
+	/* Get the specific init parameters of QMP phy */
+	match = of_match_node(qcom_qmp_phy_of_match_table, dev->of_node);
+	qphy->cfg = match->data;
+
+	ret = qcom_qmp_phy_clk_init(dev);
+	if (ret) {
+		dev_err(dev, "clock init failed\n");
+		return ret;
+	}
+
+	ret = qcom_qmp_phy_regulator_init(dev);
+	if (ret) {
+		dev_err(dev, "regulator init failed\n");
+		return ret;
+	}
+
+	qphy->phy_rst = devm_reset_control_get(dev, "phy");
+	if (IS_ERR(qphy->phy_rst)) {
+		dev_err(dev, "failed to get phy core reset\n");
+		return PTR_ERR(qphy->phy_rst);
+	}
+
+	qphy->phycom_rst = devm_reset_control_get(dev, "common");
+	if (IS_ERR(qphy->phycom_rst)) {
+		dev_err(dev, "failed to get phy common reset\n");
+		return PTR_ERR(qphy->phycom_rst);
+	}
+
+	qphy->phycfg_rst = devm_reset_control_get(dev, "cfg");
+	if (IS_ERR(qphy->phycfg_rst)) {
+		dev_err(dev, "failed to get phy ahb cfg reset\n");
+		qphy->phycfg_rst = NULL;
+	}
+
+	qphy->phys = devm_kcalloc(dev, qphy->cfg->nlanes,
+					sizeof(*qphy->phys), GFP_KERNEL);
+	if (!qphy->phys)
+		return -ENOMEM;
+
+	for (id = 0; id < qphy->cfg->nlanes; id++) {
+		struct phy *generic_phy;
+		struct qmp_phy_desc *phy_desc;
+		char prop_name[MAX_PROP_NAME];
+
+		phy_desc = devm_kzalloc(dev, sizeof(*phy_desc), GFP_KERNEL);
+		if (!phy_desc) {
+			ret = -ENOMEM;
+			goto put_child;
+		}
+
+		phy_desc->tx = base + qphy->cfg->tx_offsets[id];
+		phy_desc->rx = base + qphy->cfg->rx_offsets[id];
+		phy_desc->pcs = base + qphy->cfg->pcs_offsets[id];
+
+		/*
+		 * Get PHY's Pipe clock, if any; USB3 and PCIe are PIPE
+		 * based phys, so they essentially have pipe clock
+		 */
+		memset(&prop_name, 0, sizeof(prop_name));
+		snprintf(prop_name, MAX_PROP_NAME, "pipe%d", id);
+		phy_desc->pipe_clk = devm_clk_get(dev, prop_name);
+		if (IS_ERR(phy_desc->pipe_clk)) {
+			if (qphy->cfg->type == PHY_TYPE_PCIE ||
+			    qphy->cfg->type == PHY_TYPE_USB3) {
+				ret = PTR_ERR(phy_desc->pipe_clk);
+				if (ret != -EPROBE_DEFER)
+					dev_err(dev,
+					"failed to get lane%d pipe_clk\n", id);
+				return ret;
+			} else {
+				phy_desc->pipe_clk = NULL;
+			}
+		}
+
+		/* Get lane reset, if any */
+		memset(&prop_name, 0, sizeof(prop_name));
+		snprintf(prop_name, MAX_PROP_NAME, "lane%d", id);
+		phy_desc->lane_rst = devm_reset_control_get(dev, prop_name);
+		if (IS_ERR(phy_desc->lane_rst)) {
+			if (qphy->cfg->type == PHY_TYPE_PCIE) {
+				dev_err(dev, "failed to get lane%d reset\n",
+									id);
+				ret = PTR_ERR(phy_desc->lane_rst);
+				goto put_child;
+			} else {
+				phy_desc->lane_rst = NULL;
+			}
+		}
+
+		generic_phy = devm_phy_create(dev, NULL, &qcom_qmp_phy_gen_ops);
+		if (IS_ERR(generic_phy)) {
+			ret = PTR_ERR(generic_phy);
+			dev_err(dev, "failed to create qphy %d\n", ret);
+			goto put_child;
+		}
+
+		phy_desc->phy = generic_phy;
+		phy_desc->index = id;
+		phy_desc->qphy = qphy;
+		phy_set_drvdata(generic_phy, phy_desc);
+		qphy->phys[id] = phy_desc;
+	}
+
+	phy_provider = devm_of_phy_provider_register(dev, qcom_qmp_phy_xlate);
+	if (IS_ERR(phy_provider)) {
+		ret = PTR_ERR(phy_provider);
+		dev_err(dev, "failed to register qphy %d\n", ret);
+		goto put_child;
+	}
+
+put_child:
+	of_node_put(child);
+	return ret;
+}
+
+static struct platform_driver qcom_qmp_phy_driver = {
+	.probe		= qcom_qmp_phy_probe,
+	.driver = {
+		.name	= "qcom_qmp_phy",
+		.of_match_table = of_match_ptr(qcom_qmp_phy_of_match_table),
+	},
+};
+
+module_platform_driver(qcom_qmp_phy_driver);
+
+MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>");
+MODULE_DESCRIPTION("Qualcomm QMP PHY driver");
+MODULE_LICENSE("GPL v2");
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] phy: qcom-qmp: new qmp phy driver for qcom-chipsets
  2016-10-19 10:43 ` [PATCH 2/2] phy: qcom-qmp: new qmp phy driver for qcom-chipsets Vivek Gautam
@ 2016-10-19 10:54   ` Vivek Gautam
  2016-10-20  4:17     ` Vivek Gautam
  2016-10-26 20:11   ` Kishon Vijay Abraham I
       [not found]   ` <1476873827-7191-3-git-send-email-vivek.gautam-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
  2 siblings, 1 reply; 19+ messages in thread
From: Vivek Gautam @ 2016-10-19 10:54 UTC (permalink / raw)
  To: kishon, robh+dt, mark.rutland
  Cc: devicetree, linux-kernel, linux-arm-msm, srinivas.kandagatla

CC: Srinivas Kandagatla

On 10/19/2016 04:13 PM, Vivek Gautam wrote:
> Qualcomm SOCs have QMP phy controller that provides support
> to a number of controller, viz. PCIe, UFS, and USB.
> Add a new driver, based on generic phy framework, for this
> phy controller.
>
> USB3-phy changes: Based on phy-msm-ssusb-qmp driver available on
> msm-4.4 kernel @codeaurora[1].
> PCIe-phy changes: Based on msm8996-pcie-phy driver posted by
> Srinivas [2].
>
> [1] https://source.codeaurora.org/quic/la/kernel/msm-4.4/log/?h=caf/3.18/msm-3.18
> [2] https://patchwork.kernel.org/patch/9318947/
>
> Signed-off-by: Vivek Gautam <vivek.gautam@codeaurora.org>
> Cc: Kishon Vijay Abraham I <kishon@ti.com>
> ---
>   .../devicetree/bindings/phy/qcom-qmp-phy.txt       |   61 ++
>   drivers/phy/Kconfig                                |    8 +
>   drivers/phy/Makefile                               |    1 +
>   drivers/phy/phy-qcom-qmp.c                         | 1154 ++++++++++++++++++++
>   4 files changed, 1224 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
>   create mode 100644 drivers/phy/phy-qcom-qmp.c
>
> diff --git a/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
> new file mode 100644
> index 0000000..90214aa
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
> @@ -0,0 +1,61 @@
> +Qualcomm QMP PHY
> +----------------
> +
> +QMP phy controller supports physical layer functionality for a number of
> +controllers on Qualcomm chipsets, such as, PCIe, UFS, and USB.
> +
> +Required properties:
> + - compatible: compatible list, contains:
> +	       "qcom,msm8996-qmp-pcie-phy" for 14nm PCIe phy on msm8996,
> +	       "qcom,msm8996-qmp-usb3-phy" for 14nm USB3 phy on msm8996.
> + - reg: offset and length of the PHY register set.
> + - #phy-cells: must be 1
> +    - Cell after phy phandle should be the port (lane) number.
> + - clocks: a list of phandles and clock-specifier pairs,
> +	   one for each entry in clock-names.
> + - clock-names: must be "cfg_ahb" for phy config clock,
> +			"aux" for phy aux clock,
> +			"ref_clk" for 19.2 MHz ref clk,
> +			"ref_clk_src" for reference clock source,
> +			"pipe<port-number>" for pipe clock specific to
> +			each port/lane (Optional).
> + - resets: a list of phandles and reset controller specifier pairs,
> +	   one for each entry in reset-names.
> + - reset-names: must be "phy" for reset of phy block,
> +			"common" for phy common block reset,
> +			"cfg" for phy's ahb cfg block reset (Optional).
> +			"port<port-number>" for reset specific to
> +			each port/lane. (Optional)
> + - vdda-phy-supply: Phandle to a regulator supply to PHY core block.
> + - vdda-pll-supply: Phandle to 1.8V regulator supply to PHY refclk pll block.
> +
> +Optional properties:
> + - vddp-ref-clk-supply: Phandle to a regulator supply to any specific refclk
> +			pll block.
> +
> +Example:
> +	pcie_phy: pciephy@34000 {
> +		compatible = "qcom,qmp-14nm-pcie-phy";
> +		reg = <0x034000 0x3fff>;
> +		#phy-cells = <1>;
> +
> +		clocks = <&gcc GCC_PCIE_PHY_AUX_CLK>,
> +			<&gcc GCC_PCIE_PHY_CFG_AHB_CLK>,
> +			<&gcc GCC_PCIE_0_PIPE_CLK>,
> +			<&gcc GCC_PCIE_1_PIPE_CLK>,
> +			<&gcc GCC_PCIE_2_PIPE_CLK>;
> +		clock-names = "aux", "cfg_ahb",
> +				"pipe0", "pipe1", "pipe2";
> +
> +		vdda-phy-supply = <&pm8994_l28>;
> +		vdda-pll-supply = <&pm8994_l12>;
> +
> +		resets = <&gcc GCC_PCIE_PHY_BCR>,
> +			<&gcc GCC_PCIE_PHY_COM_BCR>,
> +			<&gcc GCC_PCIE_PHY_COM_NOCSR_BCR>,
> +			<&gcc GCC_PCIE_0_PHY_BCR>,
> +			<&gcc GCC_PCIE_1_PHY_BCR>,
> +			<&gcc GCC_PCIE_2_PHY_BCR>;
> +		reset-names = "phy", "common", "cfg",
> +				"lane0", "lane1", "lane2";
> +	};
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index 5547984..d5e2b50f 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -446,6 +446,14 @@ config PHY_STIH41X_USB
>   	  Enable this to support the USB transceiver that is part of
>   	  STMicroelectronics STiH41x SoC series.
>   
> +config PHY_QCOM_QMP
> +	tristate "Qualcomm QMP PHY Driver"
> +	depends on OF && (ARCH_QCOM || COMPILE_TEST)
> +	select GENERIC_PHY
> +	help
> +	  Enable this to support the QMP PHY transceiver that is used
> +	  with controllers such as PCIe, UFS, and USB on Qualcomm chips.
> +
>   config PHY_QCOM_QUSB2
>   	tristate "Qualcomm QUSB2 PHY Driver"
>   	depends on OF && (ARCH_QCOM || COMPILE_TEST)
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index 848489d..fde9fba 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -51,6 +51,7 @@ obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+= phy-spear1340-miphy.o
>   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_QMP) 	+= phy-qcom-qmp.o
>   obj-$(CONFIG_PHY_QCOM_QUSB2) 	+= phy-qcom-qusb2.o
>   obj-$(CONFIG_PHY_QCOM_UFS) 	+= phy-qcom-ufs.o
>   obj-$(CONFIG_PHY_QCOM_UFS) 	+= phy-qcom-ufs-qmp-20nm.o
> diff --git a/drivers/phy/phy-qcom-qmp.c b/drivers/phy/phy-qcom-qmp.c
> new file mode 100644
> index 0000000..7e89179
> --- /dev/null
> +++ b/drivers/phy/phy-qcom-qmp.c
> @@ -0,0 +1,1154 @@
> +/*
> + * Copyright (c) 2016, The Linux Foundation. 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/delay.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/reset.h>
> +#include <linux/slab.h>
> +
> +#include <dt-bindings/phy/phy.h>
> +
> +/* QMP PHY QSERDES COM registers */
> +#define QSERDES_COM_BG_TIMER				0x00c
> +#define QSERDES_COM_SSC_EN_CENTER			0x010
> +#define QSERDES_COM_SSC_ADJ_PER1			0x014
> +#define QSERDES_COM_SSC_ADJ_PER2			0x018
> +#define QSERDES_COM_SSC_PER1				0x01c
> +#define QSERDES_COM_SSC_PER2				0x020
> +#define QSERDES_COM_SSC_STEP_SIZE1			0x024
> +#define QSERDES_COM_SSC_STEP_SIZE2			0x028
> +#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN			0x034
> +#define QSERDES_COM_CLK_ENABLE1				0x038
> +#define QSERDES_COM_SYS_CLK_CTRL			0x03c
> +#define QSERDES_COM_SYSCLK_BUF_ENABLE			0x040
> +#define QSERDES_COM_PLL_IVCO				0x048
> +#define QSERDES_COM_LOCK_CMP1_MODE0			0x04c
> +#define QSERDES_COM_LOCK_CMP2_MODE0			0x050
> +#define QSERDES_COM_LOCK_CMP3_MODE0			0x054
> +#define QSERDES_COM_LOCK_CMP1_MODE1			0x058
> +#define QSERDES_COM_LOCK_CMP2_MODE1			0x05c
> +#define QSERDES_COM_LOCK_CMP3_MODE1			0x060
> +#define QSERDES_COM_BG_TRIM				0x070
> +#define QSERDES_COM_CLK_EP_DIV				0x074
> +#define QSERDES_COM_CP_CTRL_MODE0			0x078
> +#define QSERDES_COM_CP_CTRL_MODE1			0x07c
> +#define QSERDES_COM_PLL_RCTRL_MODE0			0x084
> +#define QSERDES_COM_PLL_RCTRL_MODE1			0x088
> +#define QSERDES_COM_PLL_CCTRL_MODE0			0x090
> +#define QSERDES_COM_PLL_CCTRL_MODE1			0x094
> +#define QSERDES_COM_SYSCLK_EN_SEL			0x0ac
> +#define QSERDES_COM_RESETSM_CNTRL			0x0b4
> +#define QSERDES_COM_RESTRIM_CTRL			0x0bc
> +#define QSERDES_COM_RESCODE_DIV_NUM			0x0c4
> +#define QSERDES_COM_LOCK_CMP_EN				0x0c8
> +#define QSERDES_COM_LOCK_CMP_CFG			0x0cc
> +#define QSERDES_COM_DEC_START_MODE0			0x0d0
> +#define QSERDES_COM_DEC_START_MODE1			0x0d4
> +#define QSERDES_COM_DIV_FRAC_START1_MODE0		0x0dc
> +#define QSERDES_COM_DIV_FRAC_START2_MODE0		0x0e0
> +#define QSERDES_COM_DIV_FRAC_START3_MODE0		0x0e4
> +#define QSERDES_COM_DIV_FRAC_START1_MODE1		0x0e8
> +#define QSERDES_COM_DIV_FRAC_START2_MODE1		0x0ec
> +#define QSERDES_COM_DIV_FRAC_START3_MODE1		0x0f0
> +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0		0x108
> +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0		0x10c
> +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1		0x110
> +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1		0x114
> +#define QSERDES_COM_VCO_TUNE_CTRL			0x124
> +#define QSERDES_COM_VCO_TUNE_MAP			0x128
> +#define QSERDES_COM_VCO_TUNE1_MODE0			0x12c
> +#define QSERDES_COM_VCO_TUNE2_MODE0			0x130
> +#define QSERDES_COM_VCO_TUNE1_MODE1			0x134
> +#define QSERDES_COM_VCO_TUNE2_MODE1			0x138
> +#define QSERDES_COM_VCO_TUNE_TIMER1			0x144
> +#define QSERDES_COM_VCO_TUNE_TIMER2			0x148
> +#define QSERDES_COM_BG_CTRL				0x170
> +#define QSERDES_COM_CLK_SELECT				0x174
> +#define QSERDES_COM_HSCLK_SEL				0x178
> +#define QSERDES_COM_CORECLK_DIV				0x184
> +#define QSERDES_COM_CORE_CLK_EN				0x18c
> +#define QSERDES_COM_C_READY_STATUS			0x190
> +#define QSERDES_COM_CMN_CONFIG				0x194
> +#define QSERDES_COM_SVS_MODE_CLK_SEL			0x19c
> +#define QSERDES_COM_DEBUG_BUS0				0x1a0
> +#define QSERDES_COM_DEBUG_BUS1				0x1a4
> +#define QSERDES_COM_DEBUG_BUS2				0x1a8
> +#define QSERDES_COM_DEBUG_BUS3				0x1ac
> +#define QSERDES_COM_DEBUG_BUS_SEL			0x1b0
> +#define QSERDES_COM_CORECLK_DIV_MODE1			0x1bc
> +
> +/* QMP PHY TX registers */
> +#define QSERDES_TX_RES_CODE_LANE_OFFSET			0x054
> +#define QSERDES_TX_DEBUG_BUS_SEL			0x064
> +#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN	0x068
> +#define QSERDES_TX_LANE_MODE				0x094
> +#define QSERDES_TX_RCV_DETECT_LVL_2			0x0ac
> +
> +/* QMP PHY RX registers */
> +#define QSERDES_RX_UCDR_SO_GAIN_HALF			0x010
> +#define QSERDES_RX_UCDR_SO_GAIN				0x01c
> +#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN		0x040
> +#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE	0x048
> +#define QSERDES_RX_RX_TERM_BW				0x090
> +#define QSERDES_RX_RX_EQ_GAIN1_LSB			0x0c4
> +#define QSERDES_RX_RX_EQ_GAIN1_MSB			0x0c8
> +#define QSERDES_RX_RX_EQ_GAIN2_LSB			0x0cc
> +#define QSERDES_RX_RX_EQ_GAIN2_MSB			0x0d0
> +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2		0x0d8
> +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3		0x0dc
> +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4		0x0e0
> +#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1		0x108
> +#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2		0x10c
> +#define QSERDES_RX_SIGDET_ENABLES			0x110
> +#define QSERDES_RX_SIGDET_CNTRL				0x114
> +#define QSERDES_RX_SIGDET_LVL				0x118
> +#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL		0x11c
> +#define QSERDES_RX_RX_BAND				0x120
> +#define QSERDES_RX_RX_INTERFACE_MODE			0x12c
> +
> +/* QMP PHY PCS registers */
> +#define QPHY_SW_RESET					0x00
> +#define QPHY_POWER_DOWN_CONTROL				0x04
> +#define QPHY_START_CTRL					0x08
> +#define QPHY_TXDEEMPH_M6DB_V0				0x24
> +#define QPHY_TXDEEMPH_M3P5DB_V0				0x28
> +#define QPHY_ENDPOINT_REFCLK_DRIVE			0x54
> +#define QPHY_RX_IDLE_DTCT_CNTRL				0x58
> +#define QPHY_POWER_STATE_CONFIG1			0x60
> +#define QPHY_POWER_STATE_CONFIG2			0x64
> +#define QPHY_POWER_STATE_CONFIG4			0x6c
> +#define QPHY_LOCK_DETECT_CONFIG1			0x80
> +#define QPHY_LOCK_DETECT_CONFIG2			0x84
> +#define QPHY_LOCK_DETECT_CONFIG3			0x88
> +#define QPHY_PWRUP_RESET_DLY_TIME_AUXCLK		0xa0
> +#define QPHY_LP_WAKEUP_DLY_TIME_AUXCLK			0xa4
> +
> +/* PHY_SW_RESET bit */
> +#define PHY_SW_RESET				BIT(0)
> +/* PHY_POWER_DOWN_CONTROL */
> +#define PHY_SW_PWRDN				BIT(0)
> +#define PHY_REFCLK_DRV_DSBL			BIT(1)
> +/* PHY_START_CONTROL bits */
> +#define PHY_SERDES_START			BIT(0)
> +#define PHY_PCS_START				BIT(1)
> +#define PHY_PLL_READY_GATE_EN			BIT(3)
> +/* PHY_PCS_STATUS bit */
> +#define MASK_PHYSTATUS				BIT(6)
> +/* PCS_READY_STATUS bit */
> +#define MASK_COM_PCS_READY			BIT(0)
> +
> +#define REFCLK_STABILIZATION_DELAY_US_MIN	1000
> +#define REFCLK_STABILIZATION_DELAY_US_MAX	1005
> +#define PHY_READY_TIMEOUT_COUNT			10
> +#define POWER_DOWN_DELAY_US_MIN			10
> +#define POWER_DOWN_DELAY_US_MAX			11
> +
> +#define MAX_PROP_NAME		32
> +
> +struct qmp_phy_init_tbl {
> +	unsigned int reg_offset;
> +	unsigned int cfg_val;
> +	/*
> +	 * register part of layout ?
> +	 * if yes, then reg_offset gives index in the reg-layout
> +	 */
> +	int in_layout;
> +};
> +#define QCOM_QMP_PHY_INIT_CFG(reg, val) \
> +	{				\
> +		.reg_offset = reg,	\
> +		.cfg_val = val,		\
> +	}
> +#define QCOM_QMP_PHY_INIT_CFG_L(reg, val) \
> +	{				  \
> +		.reg_offset = reg,	  \
> +		.cfg_val = val,		  \
> +		.in_layout = 1,		  \
> +	}
> +
> +/* set of registers with offsets different per-PHY */
> +enum qphy_reg_layout {
> +	/* Common block control registers */
> +	QPHY_COM_SW_RESET,
> +	QPHY_COM_POWER_DOWN_CONTROL,
> +	QPHY_COM_START_CONTROL,
> +	QPHY_COM_PCS_READY_STATUS,
> +	/* PCS registers */
> +	QPHY_PLL_LOCK_CHK_DLY_TIME,
> +	QPHY_FLL_CNTRL1,
> +	QPHY_FLL_CNTRL2,
> +	QPHY_FLL_CNT_VAL_L,
> +	QPHY_FLL_CNT_VAL_H_TOL,
> +	QPHY_FLL_MAN_CODE,
> +	QPHY_PCS_READY_STATUS,
> +};
> +
> +unsigned int pciephy_regs_layout[] = {
> +	[QPHY_COM_SW_RESET]		= 0x400,
> +	[QPHY_COM_POWER_DOWN_CONTROL]	= 0x404,
> +	[QPHY_COM_START_CONTROL]	= 0x408,
> +	[QPHY_COM_PCS_READY_STATUS]	= 0x448,
> +	[QPHY_PLL_LOCK_CHK_DLY_TIME]	= 0xa8,
> +	[QPHY_FLL_CNTRL1]		= 0xc4,
> +	[QPHY_FLL_CNTRL2]		= 0xc8,
> +	[QPHY_FLL_CNT_VAL_L]		= 0xcc,
> +	[QPHY_FLL_CNT_VAL_H_TOL]	= 0xd0,
> +	[QPHY_FLL_MAN_CODE]		= 0xd4,
> +	[QPHY_PCS_READY_STATUS]		= 0x174,
> +};
> +
> +unsigned int usb3phy_regs_layout[] = {
> +	[QPHY_FLL_CNTRL1]		= 0xc0,
> +	[QPHY_FLL_CNTRL2]		= 0xc4,
> +	[QPHY_FLL_CNT_VAL_L]		= 0xc8,
> +	[QPHY_FLL_CNT_VAL_H_TOL]	= 0xcc,
> +	[QPHY_FLL_MAN_CODE]		= 0xd0,
> +	[QPHY_PCS_READY_STATUS]		= 0x17c,
> +};
> +
> +static struct qmp_phy_init_tbl pciephy_serdes_init_tbl[] = {
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1c),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x42),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x1f),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x01),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x09),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x1a),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x0a),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x02),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1f),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x04),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x02),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x2f),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x19),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x15),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_EP_DIV, 0x19),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x40),
> +};
> +
> +static struct qmp_phy_init_tbl pciephy_tx_init_tbl[] = {
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06),
> +};
> +
> +static struct qmp_phy_init_tbl pciephy_rx_init_tbl[] = {
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x1c),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x01),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xdb),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_BAND, 0x18),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN_HALF, 0x04),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x14),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x19),
> +};
> +
> +static struct qmp_phy_init_tbl pciephy_pcs_init_tbl[] = {
> +	QCOM_QMP_PHY_INIT_CFG(QPHY_RX_IDLE_DTCT_CNTRL, 0x4c),
> +	QCOM_QMP_PHY_INIT_CFG(QPHY_PWRUP_RESET_DLY_TIME_AUXCLK, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QPHY_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01),
> +
> +	QCOM_QMP_PHY_INIT_CFG_L(QPHY_PLL_LOCK_CHK_DLY_TIME, 0x05),
> +
> +	QCOM_QMP_PHY_INIT_CFG(QPHY_ENDPOINT_REFCLK_DRIVE, 0x05),
> +	QCOM_QMP_PHY_INIT_CFG(QPHY_POWER_DOWN_CONTROL, 0x02),
> +	QCOM_QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG4, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG1, 0xa3),
> +	QCOM_QMP_PHY_INIT_CFG(QPHY_TXDEEMPH_M3P5DB_V0, 0x0e),
> +};
> +
> +static struct qmp_phy_init_tbl usb3phy_serdes_init_tbl[] = {
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x04),
> +	/* PLL and Loop filter settings */
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x15),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x34),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a),
> +	/* SSC settings */
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0xde),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x07),
> +};
> +
> +static struct qmp_phy_init_tbl usb3phy_tx_init_tbl[] = {
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_TX_RCV_DETECT_LVL_2, 0x12),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06),
> +};
> +
> +static struct qmp_phy_init_tbl usb3phy_rx_init_tbl[] = {
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xbb),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x03),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x18),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x16),
> +};
> +
> +static struct qmp_phy_init_tbl usb3phy_pcs_init_tbl[] = {
> +	/* FLL settings */
> +	QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_CNTRL2, 0x03),
> +	QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_CNTRL1, 0x02),
> +	QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_CNT_VAL_L, 0x09),
> +	QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_CNT_VAL_H_TOL, 0x42),
> +	QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_MAN_CODE, 0x85),
> +
> +	/* Lock Det settings */
> +	QCOM_QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG1, 0xd1),
> +	QCOM_QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG2, 0x1f),
> +	QCOM_QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG3, 0x47),
> +	QCOM_QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG2, 0x08),
> +};
> +
> +/**
> + * struct qmp_phy_init_cfg:- per-PHY init config.
> + */
> +struct qmp_phy_init_cfg {
> +	/*
> +	 * @type: phy-type - PCIE/UFS/USB
> +	 * @nlanes: number of lanes provided by phy
> +	 * @tx_offsets: array of offsets of Tx block from PHY base
> +	 * @rx_offsets: array of offsets of Rx block from PHY base
> +	 * @pcs_offsets: array of offsets of PCS block from PHY base
> +	 */
> +	unsigned int type;
> +	int nlanes;
> +	unsigned int *tx_offsets;
> +	unsigned int *rx_offsets;
> +	unsigned int *pcs_offsets;
> +
> +	/* Initialization sequence for PHY blocks - Serdes, tx, rx, pcs */
> +	struct qmp_phy_init_tbl *phy_init_serdes_tbl;
> +	int phy_init_serdes_tbl_sz;
> +	struct qmp_phy_init_tbl *phy_init_tx_tbl;
> +	int phy_init_tx_tbl_sz;
> +	struct qmp_phy_init_tbl *phy_init_rx_tbl;
> +	int phy_init_rx_tbl_sz;
> +	struct qmp_phy_init_tbl *phy_init_pcs_tbl;
> +	int phy_init_pcs_tbl_sz;
> +
> +	/* array of registers with different offsets */
> +	unsigned int *regs;
> +
> +	unsigned int mask_start_ctrl;
> +	unsigned int mask_pwr_dn_ctrl;
> +	/* true, if PHY has a separate PHY_COM_CNTRL block */
> +	bool has_phy_com_ctrl;
> +};
> +
> +/**
> + * struct qmp_phy_desc:- per-lane phy-descriptor.
> + *
> + * @phy: pointer to generic phy
> + * @tx: pointer to iomapped memory space for PHY's tx
> + * @rx: pointer to iomapped memory space for PHY's rx
> + * @pcs: pointer to iomapped memory space for PHY's pcs
> + * @pipe_clk: pointer to pipe lock
> + * @index: lane index
> + * @qphy: pointer to QMP phy to which this lane belongs
> + * @lane_rst: pointer to lane's reset controller
> + */
> +struct qmp_phy_desc {
> +	struct phy *phy;
> +	void __iomem *tx;
> +	void __iomem *rx;
> +	void __iomem *pcs;
> +	struct clk *pipe_clk;
> +	unsigned int index;
> +	struct qcom_qmp_phy *qphy;
> +	struct reset_control *lane_rst;
> +};
> +
> +/**
> + * struct qcom_qmp_phy:- structure holding QMP PHY attributes.
> + *
> + * @dev: pointer to device
> + * @serdes: pointer to iomapped memory space for phy's serdes
> + *
> + * @aux_clk: pointer to phy core clock
> + * @cfg_ahb_clk: pointer to AHB2PHY interface clock
> + * @ref_clk: pointer to reference clock
> + * @ref_clk_src: pointer to source to reference clock
> + * @clk_enabled: check if clocks are enabled or not
> + *
> + * @vdda_phy: vdd supply to the phy core block
> + * @vdda_pll: 1.8V vdd supply to ref_clk block
> + * @vddp_ref_clk: vdd supply to specific ref_clk block
> + *
> + * @phy_rst: Pointer to phy reset control
> + * @phycom_rst: Pointer to phy common reset control
> + * @phycfg_rst: Pointer to phy ahb cfg reset control (Optional)
> + *
> + * @cfg: pointer to init config for each phys
> + * @phys: array of pointer to per-lane phy descriptors
> + * @phy_mutex: mutex lock for PHY common block initialization
> + * @init_count: Phy common block initialization count
> + */
> +struct qcom_qmp_phy {
> +	struct device *dev;
> +	void __iomem *serdes;
> +
> +	struct clk *aux_clk;
> +	struct clk *cfg_ahb_clk;
> +	struct clk *ref_clk;
> +	struct clk *ref_clk_src;
> +	bool clk_enabled;
> +
> +	struct regulator *vdda_phy;
> +	struct regulator *vdda_pll;
> +	struct regulator *vddp_ref_clk;
> +
> +	struct reset_control *phy_rst;
> +	struct reset_control *phycom_rst;
> +	struct reset_control *phycfg_rst;
> +
> +	const struct qmp_phy_init_cfg *cfg;
> +	struct qmp_phy_desc **phys;
> +
> +	struct mutex phy_mutex;
> +	int init_count;
> +};
> +
> +static inline void qphy_setbits(u32 bitmask, void __iomem *reg)
> +{
> +	u32 val;
> +
> +	val = readl_relaxed(reg);
> +	val |= bitmask;
> +	writel_relaxed(val, reg);
> +}
> +
> +static inline void qphy_clrbits(u32 bitmask, void __iomem *reg)
> +{
> +	u32 val;
> +
> +	val = readl_relaxed(reg);
> +	val &= ~bitmask;
> +	writel_relaxed(val, reg);
> +}
> +
> +unsigned int msm8996_pciephy_tx_offsets[] = { 0x1000, 0x2000, 0x3000 };
> +unsigned int msm8996_pciephy_rx_offsets[] = { 0x1200, 0x2200, 0x3200 };
> +unsigned int msm8996_pciephy_pcs_offsets[] = { 0x1400, 0x2400, 0x3400 };
> +
> +unsigned int msm8996_usb3phy_tx_offsets[] = { 0x200 };
> +unsigned int msm8996_usb3phy_rx_offsets[] = { 0x400 };
> +unsigned int msm8996_usb3phy_pcs_offsets[] = { 0x600 };
> +
> +const struct qmp_phy_init_cfg pciephy_init_cfg = {
> +	.type			= PHY_TYPE_PCIE,
> +	.nlanes			= 3,
> +	.tx_offsets		= msm8996_pciephy_tx_offsets,
> +	.rx_offsets		= msm8996_pciephy_rx_offsets,
> +	.pcs_offsets		= msm8996_pciephy_pcs_offsets,
> +
> +	.phy_init_serdes_tbl	= pciephy_serdes_init_tbl,
> +	.phy_init_serdes_tbl_sz	= ARRAY_SIZE(pciephy_serdes_init_tbl),
> +	.phy_init_tx_tbl	= pciephy_tx_init_tbl,
> +	.phy_init_tx_tbl_sz	= ARRAY_SIZE(pciephy_tx_init_tbl),
> +	.phy_init_rx_tbl	= pciephy_rx_init_tbl,
> +	.phy_init_rx_tbl_sz	= ARRAY_SIZE(pciephy_rx_init_tbl),
> +	.phy_init_pcs_tbl	= pciephy_pcs_init_tbl,
> +	.phy_init_pcs_tbl_sz	= ARRAY_SIZE(pciephy_pcs_init_tbl),
> +	.has_phy_com_ctrl	= true,
> +	.regs			= pciephy_regs_layout,
> +	.mask_start_ctrl	= (PHY_PCS_START | PHY_PLL_READY_GATE_EN),
> +	.mask_pwr_dn_ctrl	= (PHY_SW_PWRDN | PHY_REFCLK_DRV_DSBL),
> +};
> +
> +const struct qmp_phy_init_cfg usb3phy_init_cfg = {
> +	.type			= PHY_TYPE_USB3,
> +	.nlanes			= 1,
> +	.tx_offsets		= msm8996_usb3phy_tx_offsets,
> +	.rx_offsets		= msm8996_usb3phy_rx_offsets,
> +	.pcs_offsets		= msm8996_usb3phy_pcs_offsets,
> +
> +	.phy_init_serdes_tbl	= usb3phy_serdes_init_tbl,
> +	.phy_init_serdes_tbl_sz	= ARRAY_SIZE(usb3phy_serdes_init_tbl),
> +	.phy_init_tx_tbl	= usb3phy_tx_init_tbl,
> +	.phy_init_tx_tbl_sz	= ARRAY_SIZE(usb3phy_tx_init_tbl),
> +	.phy_init_rx_tbl	= usb3phy_rx_init_tbl,
> +	.phy_init_rx_tbl_sz	= ARRAY_SIZE(usb3phy_rx_init_tbl),
> +	.phy_init_pcs_tbl	= usb3phy_pcs_init_tbl,
> +	.phy_init_pcs_tbl_sz	= ARRAY_SIZE(usb3phy_pcs_init_tbl),
> +	.regs			= usb3phy_regs_layout,
> +	.mask_start_ctrl	= (PHY_SERDES_START | PHY_PCS_START),
> +	.mask_pwr_dn_ctrl	= PHY_SW_PWRDN,
> +};
> +
> +static void qcom_qmp_phy_configure(void __iomem *base,
> +				unsigned int *regs_layout,
> +				struct qmp_phy_init_tbl init_tbl[],
> +				int init_tbl_sz)
> +{
> +	int i;
> +
> +	for (i = 0; i < init_tbl_sz; i++) {
> +		if (init_tbl[i].in_layout)
> +			writel_relaxed(init_tbl[i].cfg_val,
> +				base + regs_layout[init_tbl[i].reg_offset]);
> +		else
> +			writel_relaxed(init_tbl[i].cfg_val,
> +				base + init_tbl[i].reg_offset);
> +	}
> +
> +	/* flush buffered writes */
> +	mb();
> +}
> +
> +static int qcom_qmp_phy_poweron(struct phy *phy)
> +{
> +	struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
> +	struct qcom_qmp_phy *qphy = phydesc->qphy;
> +	int ret;
> +
> +	dev_info(&phy->dev, "Powering on QMP phy\n");
> +
> +	ret = regulator_enable(qphy->vdda_phy);
> +	if (ret) {
> +		dev_err(qphy->dev, "%s: vdda-phy enable failed, err=%d\n",
> +				__func__, ret);
> +		return ret;
> +	}
> +
> +	ret = regulator_enable(qphy->vdda_pll);
> +	if (ret) {
> +		dev_err(qphy->dev, "%s: vdda-pll enable failed, err=%d\n",
> +				__func__, ret);
> +		goto err_vdda_pll;
> +	}
> +
> +	if (qphy->vddp_ref_clk) {
> +		ret = regulator_enable(qphy->vddp_ref_clk);
> +		if (ret) {
> +			dev_err(qphy->dev, "%s: vdda-ref-clk enable failed, err=%d\n",
> +					__func__, ret);
> +			goto err_vddp_refclk;
> +		}
> +	}
> +
> +	if (!qphy->clk_enabled) {
> +		clk_prepare_enable(qphy->ref_clk_src);
> +		clk_prepare_enable(qphy->ref_clk);
> +		clk_prepare_enable(phydesc->pipe_clk);
> +		qphy->clk_enabled = true;
> +	}
> +
> +	return 0;
> +
> +err_vddp_refclk:
> +	regulator_disable(qphy->vdda_pll);
> +err_vdda_pll:
> +	regulator_disable(qphy->vdda_phy);
> +	return ret;
> +}
> +
> +static int qcom_qmp_phy_poweroff(struct phy *phy)
> +{
> +	struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
> +	struct qcom_qmp_phy *qphy = phydesc->qphy;
> +
> +	if (qphy->clk_enabled) {
> +		clk_disable_unprepare(qphy->ref_clk_src);
> +		clk_disable_unprepare(qphy->ref_clk);
> +		clk_disable_unprepare(phydesc->pipe_clk);
> +		qphy->clk_enabled = false;
> +	}
> +
> +	if (qphy->vddp_ref_clk)
> +		regulator_disable(qphy->vddp_ref_clk);
> +
> +	regulator_disable(qphy->vdda_pll);
> +	regulator_disable(qphy->vdda_phy);
> +
> +	return 0;
> +}
> +
> +static int qcom_qmp_phy_is_ready(struct qcom_qmp_phy *qphy,
> +				void __iomem *pcs_status, u32 mask)
> +{
> +	unsigned int init_timeout;
> +
> +	init_timeout = PHY_READY_TIMEOUT_COUNT;
> +	do {
> +		if (readl_relaxed(pcs_status) & mask)
> +			break;
> +
> +		usleep_range(REFCLK_STABILIZATION_DELAY_US_MIN,
> +				 REFCLK_STABILIZATION_DELAY_US_MAX);
> +	} while (--init_timeout);
> +
> +	if (!init_timeout)
> +		return -EBUSY;
> +
> +	return 0;
> +}
> +
> +static int qcom_qmp_phy_com_init(struct qcom_qmp_phy *qphy)
> +{
> +	const struct qmp_phy_init_cfg *cfg = qphy->cfg;
> +	void __iomem *serdes = qphy->serdes;
> +	int ret;
> +
> +	mutex_lock(&qphy->phy_mutex);
> +	if (qphy->init_count++) {
> +		mutex_unlock(&qphy->phy_mutex);
> +		return 0;
> +	}
> +
> +	ret = reset_control_deassert(qphy->phy_rst);
> +	if (ret) {
> +		dev_err(qphy->dev, "phy reset deassert failed\n");
> +		return ret;
> +	}
> +
> +	ret = reset_control_deassert(qphy->phycom_rst);
> +	if (ret) {
> +		dev_err(qphy->dev, "common reset deassert failed\n");
> +		goto err_phycom_rst;
> +	}
> +
> +	if (qphy->phycfg_rst) {
> +		ret = reset_control_deassert(qphy->phycfg_rst);
> +		if (ret) {
> +			dev_err(qphy->dev, "common reset deassert failed\n");
> +			goto err_phycfg_rst;
> +		}
> +	}
> +
> +	if (cfg->has_phy_com_ctrl) {
> +		qphy_setbits(PHY_SW_PWRDN,
> +			serdes + cfg->regs[QPHY_COM_POWER_DOWN_CONTROL]);
> +		/* Make sure that above write is completed */
> +		mb();
> +	}
> +
> +	/* Serdes configuration */
> +	qcom_qmp_phy_configure(serdes, cfg->regs, cfg->phy_init_serdes_tbl,
> +				cfg->phy_init_serdes_tbl_sz);
> +
> +	if (cfg->has_phy_com_ctrl) {
> +		qphy_clrbits(PHY_SW_RESET, serdes +
> +					cfg->regs[QPHY_COM_SW_RESET]);
> +		qphy_setbits(PHY_SERDES_START | PHY_PCS_START,
> +				serdes + cfg->regs[QPHY_COM_START_CONTROL]);
> +		/* Make sure that above write is completed */
> +		mb();
> +
> +		ret = qcom_qmp_phy_is_ready(qphy, serdes +
> +					cfg->regs[QPHY_COM_PCS_READY_STATUS],
> +					MASK_COM_PCS_READY);
> +		if (ret) {
> +			dev_err(qphy->dev,
> +				"common control block init timed-out\n");
> +			goto err_phy_comctrl;
> +		}
> +	}
> +
> +	mutex_unlock(&qphy->phy_mutex);
> +
> +	return 0;
> +
> +err_phy_comctrl:
> +	if (qphy->phycfg_rst)
> +		reset_control_assert(qphy->phycfg_rst);
> +err_phycfg_rst:
> +	reset_control_assert(qphy->phycom_rst);
> +err_phycom_rst:
> +	reset_control_assert(qphy->phy_rst);
> +	return ret;
> +}
> +
> +static int qcom_qmp_phy_com_exit(struct qcom_qmp_phy *qphy)
> +{
> +	const struct qmp_phy_init_cfg *cfg = qphy->cfg;
> +	void __iomem *serdes = qphy->serdes;
> +
> +	mutex_lock(&qphy->phy_mutex);
> +	if (--qphy->init_count) {
> +		mutex_unlock(&qphy->phy_mutex);
> +		return 0;
> +	}
> +
> +	if (cfg->has_phy_com_ctrl) {
> +		qphy_setbits(PHY_SERDES_START | PHY_PCS_START,
> +				serdes + cfg->regs[QPHY_COM_START_CONTROL]);
> +		qphy_clrbits(PHY_SW_RESET, serdes +
> +					cfg->regs[QPHY_COM_SW_RESET]);
> +		qphy_setbits(PHY_SW_PWRDN,
> +			serdes + cfg->regs[QPHY_COM_POWER_DOWN_CONTROL]);
> +
> +		/* Make sure that above writes are completed */
> +		mb();
> +	}
> +
> +	reset_control_assert(qphy->phy_rst);
> +	reset_control_assert(qphy->phycom_rst);
> +	if (qphy->phycfg_rst)
> +		reset_control_assert(qphy->phycfg_rst);
> +
> +	mutex_unlock(&qphy->phy_mutex);
> +
> +	return 0;
> +}
> +
> +/* PHY Initialization */
> +static int qcom_qmp_phy_init(struct phy *phy)
> +{
> +	struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
> +	struct qcom_qmp_phy *qphy = phydesc->qphy;
> +	const struct qmp_phy_init_cfg *cfg = qphy->cfg;
> +	void __iomem *tx = phydesc->tx;
> +	void __iomem *rx = phydesc->rx;
> +	void __iomem *pcs = phydesc->pcs;
> +	int ret;
> +
> +	dev_info(qphy->dev, "Initializing QMP phy\n");
> +
> +	/* enable interface clocks to program phy */
> +	clk_prepare_enable(qphy->aux_clk);
> +	clk_prepare_enable(qphy->cfg_ahb_clk);
> +
> +	ret = qcom_qmp_phy_com_init(qphy);
> +	if (ret)
> +		goto err;
> +
> +	if (phydesc->lane_rst) {
> +		ret = reset_control_deassert(phydesc->lane_rst);
> +		if (ret) {
> +			dev_err(qphy->dev, "lane<%d> reset deassert failed\n",
> +					phydesc->index);
> +			goto err_lane_rst;
> +		}
> +	}
> +
> +	/* Tx, Rx, and PCS configurations */
> +	qcom_qmp_phy_configure(tx, cfg->regs, cfg->phy_init_tx_tbl,
> +				cfg->phy_init_tx_tbl_sz);
> +	qcom_qmp_phy_configure(rx, cfg->regs, cfg->phy_init_rx_tbl,
> +				cfg->phy_init_rx_tbl_sz);
> +	qcom_qmp_phy_configure(pcs, cfg->regs, cfg->phy_init_pcs_tbl,
> +				cfg->phy_init_pcs_tbl_sz);
> +
> +	/*
> +	 * Pull out PHY from POWER DOWN state:
> +	 * This is active low enable signal to power-down PHY.
> +	 */
> +	qphy_setbits(cfg->mask_pwr_dn_ctrl,
> +			pcs + QPHY_POWER_DOWN_CONTROL);
> +	/* XXX: 10 us delay; given in PCIE HPG only */
> +	usleep_range(POWER_DOWN_DELAY_US_MIN, POWER_DOWN_DELAY_US_MAX);
> +
> +	/* start SerDes and Phy-Coding-Sublayer */
> +	qphy_setbits(cfg->mask_start_ctrl, pcs + QPHY_START_CTRL);
> +
> +	/* Pull PHY out of reset state */
> +	qphy_clrbits(PHY_SW_RESET, pcs + QPHY_SW_RESET);
> +	/* Make sure that above writes are completed */
> +	mb();
> +
> +	ret = qcom_qmp_phy_is_ready(qphy, pcs +
> +					cfg->regs[QPHY_PCS_READY_STATUS],
> +					MASK_PHYSTATUS);
> +	if (ret) {
> +		dev_err(qphy->dev, "phy initialization timed-out\n");
> +		goto err_pcs_ready;
> +	}
> +
> +	return 0;
> +
> +err_pcs_ready:
> +	if (phydesc->lane_rst)
> +		reset_control_assert(phydesc->lane_rst);
> +err_lane_rst:
> +	qcom_qmp_phy_com_exit(qphy);
> +err:
> +	clk_disable_unprepare(qphy->cfg_ahb_clk);
> +	clk_disable_unprepare(qphy->aux_clk);
> +	return ret;
> +}
> +
> +static int qcom_qmp_phy_exit(struct phy *phy)
> +{
> +	struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
> +	struct qcom_qmp_phy *qphy = phydesc->qphy;
> +	const struct qmp_phy_init_cfg *cfg = qphy->cfg;
> +
> +	/* PHY reset */
> +	qphy_setbits(PHY_SW_RESET, phydesc->pcs + QPHY_SW_RESET);
> +
> +	/* stop SerDes and Phy-Coding-Sublayer */
> +	qphy_clrbits(cfg->mask_start_ctrl, phydesc->pcs + QPHY_START_CTRL);
> +
> +	/* Put PHY into POWER DOWN state: active low */
> +	qphy_clrbits(cfg->mask_pwr_dn_ctrl,
> +			phydesc->pcs + QPHY_POWER_DOWN_CONTROL);
> +
> +	/* Make sure that above writes are completed */
> +	mb();
> +
> +	if (phydesc->lane_rst)
> +		reset_control_assert(phydesc->lane_rst);
> +
> +	qcom_qmp_phy_com_exit(qphy);
> +
> +	clk_disable_unprepare(qphy->aux_clk);
> +	clk_disable_unprepare(qphy->cfg_ahb_clk);
> +
> +	return 0;
> +}
> +
> +
> +static int qcom_qmp_phy_regulator_init(struct device *dev)
> +{
> +	struct qcom_qmp_phy *qphy = dev_get_drvdata(dev);
> +	int ret = 0;
> +
> +	qphy->vdda_phy = devm_regulator_get(dev, "vdda-phy");
> +	if (IS_ERR(qphy->vdda_phy)) {
> +		ret = PTR_ERR(qphy->vdda_phy);
> +		dev_err(dev, "failed to get vdda-phy, %d\n", ret);
> +		return ret;
> +	}
> +
> +	qphy->vdda_pll = devm_regulator_get(dev, "vdda-pll");
> +	if (IS_ERR(qphy->vdda_pll)) {
> +		ret = PTR_ERR(qphy->vdda_pll);
> +		dev_err(dev, "failed to get vdda-pll, %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* optional regulator */
> +	qphy->vddp_ref_clk = devm_regulator_get(dev, "vddp-ref-clk");
> +	if (IS_ERR(qphy->vddp_ref_clk)) {
> +		ret = PTR_ERR(qphy->vddp_ref_clk);
> +		dev_info(dev, "failed to get vddp-ref-clk, %d\n", ret);
> +		qphy->vddp_ref_clk = NULL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int qcom_qmp_phy_clk_init(struct device *dev)
> +{
> +	struct qcom_qmp_phy *qphy = dev_get_drvdata(dev);
> +	int ret;
> +
> +	qphy->aux_clk = devm_clk_get(dev, "aux");
> +	if (IS_ERR(qphy->aux_clk)) {
> +		ret = PTR_ERR(qphy->aux_clk);
> +		if (ret != -EPROBE_DEFER)
> +			dev_err(dev, "failed to get aux_clk\n");
> +		return ret;
> +	}
> +
> +	qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb");
> +	if (IS_ERR(qphy->cfg_ahb_clk)) {
> +		ret = PTR_ERR(qphy->cfg_ahb_clk);
> +		if (ret != -EPROBE_DEFER)
> +			dev_err(dev, "failed to get cfg_ahb_clk\n");
> +		return ret;
> +	}
> +
> +	/*
> +	 * ref_clk and ref_clk_src handles may not be available in
> +	 * all hardwares. So we don't return error in these cases.
> +	 */
> +	qphy->ref_clk_src = devm_clk_get(dev, "ref_clk_src");
> +	if (IS_ERR(qphy->ref_clk_src)) {
> +		ret = PTR_ERR(qphy->ref_clk_src);
> +		if (ret != -EPROBE_DEFER) {
> +			qphy->ref_clk_src = NULL;
> +			dev_dbg(dev, "failed to get ref_clk_src\n");
> +		} else {
> +			return ret;
> +		}
> +	}
> +
> +	qphy->ref_clk = devm_clk_get(dev, "ref_clk");
> +	if (IS_ERR(qphy->ref_clk)) {
> +		ret = PTR_ERR(qphy->ref_clk);
> +		if (ret != -EPROBE_DEFER) {
> +			qphy->ref_clk = NULL;
> +			dev_dbg(dev, "failed to get ref_clk\n");
> +		} else {
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static struct phy *qcom_qmp_phy_xlate(struct device *dev,
> +					struct of_phandle_args *args)
> +{
> +	struct qcom_qmp_phy *qphy = dev_get_drvdata(dev);
> +	int i;
> +
> +	if (WARN_ON(args->args[0] >= qphy->cfg->nlanes))
> +		return ERR_PTR(-ENODEV);
> +
> +	for (i = 0; i < qphy->cfg->nlanes; i++) {
> +		if (qphy->phys[i]->index == args->args[0])
> +			break;
> +	}
> +
> +	if (i == qphy->cfg->nlanes)
> +		return ERR_PTR(-ENODEV);
> +
> +	return qphy->phys[i]->phy;
> +}
> +
> +static const struct phy_ops qcom_qmp_phy_gen_ops = {
> +	.init		= qcom_qmp_phy_init,
> +	.exit		= qcom_qmp_phy_exit,
> +	.power_on	= qcom_qmp_phy_poweron,
> +	.power_off	= qcom_qmp_phy_poweroff,
> +	.owner		= THIS_MODULE,
> +};
> +
> +static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
> +	{
> +		.compatible = "qcom,msm8996-qmp-pcie-phy",
> +		.data = &pciephy_init_cfg,
> +	}, {
> +		.compatible = "qcom,msm8996-qmp-usb3-phy",
> +		.data = &usb3phy_init_cfg,
> +	},
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, qcom_qmp_phy_of_match_table);
> +
> +static int qcom_qmp_phy_probe(struct platform_device *pdev)
> +{
> +	struct qcom_qmp_phy *qphy;
> +	struct device *dev = &pdev->dev;
> +	struct device_node *child;
> +	struct phy_provider *phy_provider;
> +	struct resource *res;
> +	const struct of_device_id *match;
> +	void __iomem *base;
> +	int ret = 0;
> +	int id;
> +
> +	qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
> +	if (!qphy)
> +		return -ENOMEM;
> +	qphy->dev = dev;
> +	dev_set_drvdata(dev, qphy);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res)
> +		return -ENODEV;
> +	base = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(base))
> +		return PTR_ERR(base);
> +
> +	/* per PHY serdes; usually located at base address */
> +	qphy->serdes = base;
> +
> +	mutex_init(&qphy->phy_mutex);
> +
> +	/* Get the specific init parameters of QMP phy */
> +	match = of_match_node(qcom_qmp_phy_of_match_table, dev->of_node);
> +	qphy->cfg = match->data;
> +
> +	ret = qcom_qmp_phy_clk_init(dev);
> +	if (ret) {
> +		dev_err(dev, "clock init failed\n");
> +		return ret;
> +	}
> +
> +	ret = qcom_qmp_phy_regulator_init(dev);
> +	if (ret) {
> +		dev_err(dev, "regulator init failed\n");
> +		return ret;
> +	}
> +
> +	qphy->phy_rst = devm_reset_control_get(dev, "phy");
> +	if (IS_ERR(qphy->phy_rst)) {
> +		dev_err(dev, "failed to get phy core reset\n");
> +		return PTR_ERR(qphy->phy_rst);
> +	}
> +
> +	qphy->phycom_rst = devm_reset_control_get(dev, "common");
> +	if (IS_ERR(qphy->phycom_rst)) {
> +		dev_err(dev, "failed to get phy common reset\n");
> +		return PTR_ERR(qphy->phycom_rst);
> +	}
> +
> +	qphy->phycfg_rst = devm_reset_control_get(dev, "cfg");
> +	if (IS_ERR(qphy->phycfg_rst)) {
> +		dev_err(dev, "failed to get phy ahb cfg reset\n");
> +		qphy->phycfg_rst = NULL;
> +	}
> +
> +	qphy->phys = devm_kcalloc(dev, qphy->cfg->nlanes,
> +					sizeof(*qphy->phys), GFP_KERNEL);
> +	if (!qphy->phys)
> +		return -ENOMEM;
> +
> +	for (id = 0; id < qphy->cfg->nlanes; id++) {
> +		struct phy *generic_phy;
> +		struct qmp_phy_desc *phy_desc;
> +		char prop_name[MAX_PROP_NAME];
> +
> +		phy_desc = devm_kzalloc(dev, sizeof(*phy_desc), GFP_KERNEL);
> +		if (!phy_desc) {
> +			ret = -ENOMEM;
> +			goto put_child;
> +		}
> +
> +		phy_desc->tx = base + qphy->cfg->tx_offsets[id];
> +		phy_desc->rx = base + qphy->cfg->rx_offsets[id];
> +		phy_desc->pcs = base + qphy->cfg->pcs_offsets[id];
> +
> +		/*
> +		 * Get PHY's Pipe clock, if any; USB3 and PCIe are PIPE
> +		 * based phys, so they essentially have pipe clock
> +		 */
> +		memset(&prop_name, 0, sizeof(prop_name));
> +		snprintf(prop_name, MAX_PROP_NAME, "pipe%d", id);
> +		phy_desc->pipe_clk = devm_clk_get(dev, prop_name);
> +		if (IS_ERR(phy_desc->pipe_clk)) {
> +			if (qphy->cfg->type == PHY_TYPE_PCIE ||
> +			    qphy->cfg->type == PHY_TYPE_USB3) {
> +				ret = PTR_ERR(phy_desc->pipe_clk);
> +				if (ret != -EPROBE_DEFER)
> +					dev_err(dev,
> +					"failed to get lane%d pipe_clk\n", id);
> +				return ret;
> +			} else {
> +				phy_desc->pipe_clk = NULL;
> +			}
> +		}
> +
> +		/* Get lane reset, if any */
> +		memset(&prop_name, 0, sizeof(prop_name));
> +		snprintf(prop_name, MAX_PROP_NAME, "lane%d", id);
> +		phy_desc->lane_rst = devm_reset_control_get(dev, prop_name);
> +		if (IS_ERR(phy_desc->lane_rst)) {
> +			if (qphy->cfg->type == PHY_TYPE_PCIE) {
> +				dev_err(dev, "failed to get lane%d reset\n",
> +									id);
> +				ret = PTR_ERR(phy_desc->lane_rst);
> +				goto put_child;
> +			} else {
> +				phy_desc->lane_rst = NULL;
> +			}
> +		}
> +
> +		generic_phy = devm_phy_create(dev, NULL, &qcom_qmp_phy_gen_ops);
> +		if (IS_ERR(generic_phy)) {
> +			ret = PTR_ERR(generic_phy);
> +			dev_err(dev, "failed to create qphy %d\n", ret);
> +			goto put_child;
> +		}
> +
> +		phy_desc->phy = generic_phy;
> +		phy_desc->index = id;
> +		phy_desc->qphy = qphy;
> +		phy_set_drvdata(generic_phy, phy_desc);
> +		qphy->phys[id] = phy_desc;
> +	}
> +
> +	phy_provider = devm_of_phy_provider_register(dev, qcom_qmp_phy_xlate);
> +	if (IS_ERR(phy_provider)) {
> +		ret = PTR_ERR(phy_provider);
> +		dev_err(dev, "failed to register qphy %d\n", ret);
> +		goto put_child;
> +	}
> +
> +put_child:
> +	of_node_put(child);
> +	return ret;
> +}
> +
> +static struct platform_driver qcom_qmp_phy_driver = {
> +	.probe		= qcom_qmp_phy_probe,
> +	.driver = {
> +		.name	= "qcom_qmp_phy",
> +		.of_match_table = of_match_ptr(qcom_qmp_phy_of_match_table),
> +	},
> +};
> +
> +module_platform_driver(qcom_qmp_phy_driver);
> +
> +MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>");
> +MODULE_DESCRIPTION("Qualcomm QMP PHY driver");
> +MODULE_LICENSE("GPL v2");

-- 
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] phy: qcom-qusb2: New driver for QUSB2 PHY on Qcom chips
  2016-10-19 10:43 ` [PATCH 1/2] phy: qcom-qusb2: New driver for QUSB2 PHY on Qcom chips Vivek Gautam
@ 2016-10-20  4:13   ` Vivek Gautam
  2016-10-26 19:56   ` Kishon Vijay Abraham I
  2016-10-26 21:16   ` Rob Herring
  2 siblings, 0 replies; 19+ messages in thread
From: Vivek Gautam @ 2016-10-20  4:13 UTC (permalink / raw)
  To: kishon, robh+dt, Mark Rutland
  Cc: devicetree, linux-kernel, linux-arm-msm, Vivek Gautam,
	fengguang.wu, kbuild-all

On Wed, Oct 19, 2016 at 4:13 PM, Vivek Gautam
<vivek.gautam@codeaurora.org> wrote:
> PHY transceiver driver for QUSB2 phy controller that provides
> HighSpeed functionality for DWC3 controller present on
> Qualcomm chipsets.
>
> This driver is based on phy-msm-qusb driver available in
> msm-4.4 kernel @codeaurora[1]
>
> [1] https://source.codeaurora.org/quic/la/kernel/msm-4.4/log/?h=caf/3.18/msm-3.18
>
> Signed-off-by: Vivek Gautam <vivek.gautam@codeaurora.org>
> Cc: Kishon Vijay Abraham I <kishon@ti.com>
> ---

[snip]

> +       qphy->ref_clk_src = devm_clk_get(dev, "ref_clk_src");
> +       if (IS_ERR(qphy->ref_clk_src)) {
> +               qphy->ref_clk_src = NULL;
> +               ret = PTR_ERR(qphy->ref_clk_src);

Pointed out by Kbuild test: ERROR: PTR_ERR applied after
initialization to constant on line

Will correct this in the next patch version.

Same for ref_clk and iface_clk.

> +               if (ret != -EPROBE_DEFER)
> +                       dev_dbg(dev, "clk get failed for ref_clk_src\n");
> +       }
> +
> +       qphy->ref_clk = devm_clk_get(dev, "ref_clk");
> +       if (IS_ERR(qphy->ref_clk)) {
> +               qphy->ref_clk = NULL;
> +               ret = PTR_ERR(qphy->ref_clk);

[snip]


Thanks
Vivek


-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] phy: qcom-qmp: new qmp phy driver for qcom-chipsets
  2016-10-19 10:54   ` Vivek Gautam
@ 2016-10-20  4:17     ` Vivek Gautam
  0 siblings, 0 replies; 19+ messages in thread
From: Vivek Gautam @ 2016-10-20  4:17 UTC (permalink / raw)
  To: kishon, robh+dt, Mark Rutland
  Cc: devicetree, linux-kernel, linux-arm-msm, Srinivas Kandagatla,
	fengguang.wu, kbuild-all, julia.lawall

On Wed, Oct 19, 2016 at 4:24 PM, Vivek Gautam
<vivek.gautam@codeaurora.org> wrote:
> CC: Srinivas Kandagatla
>
>
> On 10/19/2016 04:13 PM, Vivek Gautam wrote:
>>
>> Qualcomm SOCs have QMP phy controller that provides support
>> to a number of controller, viz. PCIe, UFS, and USB.
>> Add a new driver, based on generic phy framework, for this
>> phy controller.
>>
>> USB3-phy changes: Based on phy-msm-ssusb-qmp driver available on
>> msm-4.4 kernel @codeaurora[1].
>> PCIe-phy changes: Based on msm8996-pcie-phy driver posted by
>> Srinivas [2].
>>
>> [1]
>> https://source.codeaurora.org/quic/la/kernel/msm-4.4/log/?h=caf/3.18/msm-3.18
>> [2] https://patchwork.kernel.org/patch/9318947/
>>
>> Signed-off-by: Vivek Gautam <vivek.gautam@codeaurora.org>
>> Cc: Kishon Vijay Abraham I <kishon@ti.com>
>> ---

[snip]

>> +static int qcom_qmp_phy_com_init(struct qcom_qmp_phy *qphy)
>> +{
>> +       const struct qmp_phy_init_cfg *cfg = qphy->cfg;
>> +       void __iomem *serdes = qphy->serdes;
>> +       int ret;
>> +
>> +       mutex_lock(&qphy->phy_mutex);
>> +       if (qphy->init_count++) {
>> +               mutex_unlock(&qphy->phy_mutex);
>> +               return 0;
>> +       }
>> +
>> +       ret = reset_control_deassert(qphy->phy_rst);
>> +       if (ret) {
>> +               dev_err(qphy->dev, "phy reset deassert failed\n");
>> +               return ret;

Pointed out by Kbuild-test: drivers/phy/phy-qcom-qmp.c:677:2-8:
preceding lock on line 668

The mutex has to be unlocked for all error cases.
Will fix this in the next patch version.

[snip]


Thanks
Vivek


-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] phy: qcom-qmp: new qmp phy driver for qcom-chipsets
       [not found]   ` <1476873827-7191-3-git-send-email-vivek.gautam-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
@ 2016-10-26 13:57     ` Srinivas Kandagatla
       [not found]       ` <a969a6be-472d-a463-790b-1e2f373d19b2-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
  2016-10-26 21:18     ` Rob Herring
  1 sibling, 1 reply; 19+ messages in thread
From: Srinivas Kandagatla @ 2016-10-26 13:57 UTC (permalink / raw)
  To: Vivek Gautam, kishon-l0cyMroinI0, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA

Hi Vivek,

Thanks for consolidating qmp phy support for both usb and pcie.

On 19/10/16 11:43, Vivek Gautam wrote:
> Qualcomm SOCs have QMP phy controller that provides support
> to a number of controller, viz. PCIe, UFS, and USB.
> Add a new driver, based on generic phy framework, for this
> phy controller.
>
> USB3-phy changes: Based on phy-msm-ssusb-qmp driver available on
> msm-4.4 kernel @codeaurora[1].
> PCIe-phy changes: Based on msm8996-pcie-phy driver posted by
> Srinivas [2].
>
> [1] https://source.codeaurora.org/quic/la/kernel/msm-4.4/log/?h=caf/3.18/msm-3.18
> [2] https://patchwork.kernel.org/patch/9318947/
>
> Signed-off-by: Vivek Gautam <vivek.gautam-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> Cc: Kishon Vijay Abraham I <kishon-l0cyMroinI0@public.gmane.org>

very few minor nits which needs fixing in next version.

I have tested this patch on DB820c PCIE with ethernet and SATA ports.

Tested-by: Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

> ---
>  .../devicetree/bindings/phy/qcom-qmp-phy.txt       |   61 ++
>  drivers/phy/Kconfig                                |    8 +
>  drivers/phy/Makefile                               |    1 +
>  drivers/phy/phy-qcom-qmp.c                         | 1154 ++++++++++++++++++++
>  4 files changed, 1224 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
>  create mode 100644 drivers/phy/phy-qcom-qmp.c
>
> diff --git a/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
> new file mode 100644
> index 0000000..90214aa
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
> @@ -0,0 +1,61 @@
> +Qualcomm QMP PHY
> +----------------
> +
> +QMP phy controller supports physical layer functionality for a number of
> +controllers on Qualcomm chipsets, such as, PCIe, UFS, and USB.
> +
> +Required properties:
> + - compatible: compatible list, contains:
> +	       "qcom,msm8996-qmp-pcie-phy" for 14nm PCIe phy on msm8996,
> +	       "qcom,msm8996-qmp-usb3-phy" for 14nm USB3 phy on msm8996.
> + - reg: offset and length of the PHY register set.
> + - #phy-cells: must be 1
> +    - Cell after phy phandle should be the port (lane) number.
> + - clocks: a list of phandles and clock-specifier pairs,
> +	   one for each entry in clock-names.
> + - clock-names: must be "cfg_ahb" for phy config clock,
> +			"aux" for phy aux clock,
> +			"ref_clk" for 19.2 MHz ref clk,
> +			"ref_clk_src" for reference clock source,
> +			"pipe<port-number>" for pipe clock specific to
> +			each port/lane (Optional).

> + - resets: a list of phandles and reset controller specifier pairs,
> +	   one for each entry in reset-names.
> + - reset-names: must be "phy" for reset of phy block,
> +			"common" for phy common block reset,
> +			"cfg" for phy's ahb cfg block reset (Optional).
> +			"port<port-number>" for reset specific to
> +			each port/lane. (Optional)
> + - vdda-phy-supply: Phandle to a regulator supply to PHY core block.
> + - vdda-pll-supply: Phandle to 1.8V regulator supply to PHY refclk pll block.
> +
> +Optional properties:
> + - vddp-ref-clk-supply: Phandle to a regulator supply to any specific refclk
> +			pll block.
> +
> +Example:
> +	pcie_phy: pciephy@34000 {
> +		compatible = "qcom,qmp-14nm-pcie-phy";

Fix this according to the compatibilities listed above.

> +		reg = <0x034000 0x3fff>;
> +		#phy-cells = <1>;
> +
> +		clocks = <&gcc GCC_PCIE_PHY_AUX_CLK>,
> +			<&gcc GCC_PCIE_PHY_CFG_AHB_CLK>,
> +			<&gcc GCC_PCIE_0_PIPE_CLK>,
> +			<&gcc GCC_PCIE_1_PIPE_CLK>,
> +			<&gcc GCC_PCIE_2_PIPE_CLK>;
> +		clock-names = "aux", "cfg_ahb",
> +				"pipe0", "pipe1", "pipe2";
> +
> +		vdda-phy-supply = <&pm8994_l28>;
> +		vdda-pll-supply = <&pm8994_l12>;
> +
> +		resets = <&gcc GCC_PCIE_PHY_BCR>,
> +			<&gcc GCC_PCIE_PHY_COM_BCR>,
> +			<&gcc GCC_PCIE_PHY_COM_NOCSR_BCR>,
> +			<&gcc GCC_PCIE_0_PHY_BCR>,
> +			<&gcc GCC_PCIE_1_PHY_BCR>,
> +			<&gcc GCC_PCIE_2_PHY_BCR>;
> +		reset-names = "phy", "common", "cfg",
> +				"lane0", "lane1", "lane2";
> +	};
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index 5547984..d5e2b50f 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -446,6 +446,14 @@ config PHY_STIH41X_USB
>  	  Enable this to support the USB transceiver that is part of
>  	  STMicroelectronics STiH41x SoC series.
>
> +config PHY_QCOM_QMP
> +	tristate "Qualcomm QMP PHY Driver"
> +	depends on OF && (ARCH_QCOM || COMPILE_TEST)
> +	select GENERIC_PHY
> +	help
> +	  Enable this to support the QMP PHY transceiver that is used
> +	  with controllers such as PCIe, UFS, and USB on Qualcomm chips.
> +
>  config PHY_QCOM_QUSB2
>  	tristate "Qualcomm QUSB2 PHY Driver"
>  	depends on OF && (ARCH_QCOM || COMPILE_TEST)
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index 848489d..fde9fba 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -51,6 +51,7 @@ obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+= phy-spear1340-miphy.o
>  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_QMP) 	+= phy-qcom-qmp.o
>  obj-$(CONFIG_PHY_QCOM_QUSB2) 	+= phy-qcom-qusb2.o
>  obj-$(CONFIG_PHY_QCOM_UFS) 	+= phy-qcom-ufs.o
>  obj-$(CONFIG_PHY_QCOM_UFS) 	+= phy-qcom-ufs-qmp-20nm.o
> diff --git a/drivers/phy/phy-qcom-qmp.c b/drivers/phy/phy-qcom-qmp.c
> new file mode 100644
> index 0000000..7e89179
> --- /dev/null
> +++ b/drivers/phy/phy-qcom-qmp.c
> @@ -0,0 +1,1154 @@
> +/*
> + * Copyright (c) 2016, The Linux Foundation. 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/delay.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/reset.h>
> +#include <linux/slab.h>
> +
> +#include <dt-bindings/phy/phy.h>
We would not need this once you have proper flags in cfg.

> +
> +
> +static inline void qphy_setbits(u32 bitmask, void __iomem *reg)
> +{
> +	u32 val;
> +
> +	val = readl_relaxed(reg);
> +	val |= bitmask;
> +	writel_relaxed(val, reg);
> +}
> +
> +static inline void qphy_clrbits(u32 bitmask, void __iomem *reg)
> +{
> +	u32 val;
> +
> +	val = readl_relaxed(reg);
> +	val &= ~bitmask;
> +	writel_relaxed(val, reg);
> +}
> +
> +unsigned int msm8996_pciephy_tx_offsets[] = { 0x1000, 0x2000, 0x3000 };
> +unsigned int msm8996_pciephy_rx_offsets[] = { 0x1200, 0x2200, 0x3200 };
> +unsigned int msm8996_pciephy_pcs_offsets[] = { 0x1400, 0x2400, 0x3400 };
> +
> +unsigned int msm8996_usb3phy_tx_offsets[] = { 0x200 };
> +unsigned int msm8996_usb3phy_rx_offsets[] = { 0x400 };
> +unsigned int msm8996_usb3phy_pcs_offsets[] = { 0x600 };
> +
> +const struct qmp_phy_init_cfg pciephy_init_cfg = {
> +	.type			= PHY_TYPE_PCIE,
> +	.nlanes			= 3,
> +	.tx_offsets		= msm8996_pciephy_tx_offsets,
> +	.rx_offsets		= msm8996_pciephy_rx_offsets,
> +	.pcs_offsets		= msm8996_pciephy_pcs_offsets,
> +
> +	.phy_init_serdes_tbl	= pciephy_serdes_init_tbl,
> +	.phy_init_serdes_tbl_sz	= ARRAY_SIZE(pciephy_serdes_init_tbl),
> +	.phy_init_tx_tbl	= pciephy_tx_init_tbl,
> +	.phy_init_tx_tbl_sz	= ARRAY_SIZE(pciephy_tx_init_tbl),
> +	.phy_init_rx_tbl	= pciephy_rx_init_tbl,
> +	.phy_init_rx_tbl_sz	= ARRAY_SIZE(pciephy_rx_init_tbl),
> +	.phy_init_pcs_tbl	= pciephy_pcs_init_tbl,
> +	.phy_init_pcs_tbl_sz	= ARRAY_SIZE(pciephy_pcs_init_tbl),
> +	.has_phy_com_ctrl	= true,
> +	.regs			= pciephy_regs_layout,
> +	.mask_start_ctrl	= (PHY_PCS_START | PHY_PLL_READY_GATE_EN),
> +	.mask_pwr_dn_ctrl	= (PHY_SW_PWRDN | PHY_REFCLK_DRV_DSBL),
> +};
> +
> +const struct qmp_phy_init_cfg usb3phy_init_cfg = {
> +	.type			= PHY_TYPE_USB3,
> +	.nlanes			= 1,
> +	.tx_offsets		= msm8996_usb3phy_tx_offsets,
> +	.rx_offsets		= msm8996_usb3phy_rx_offsets,
> +	.pcs_offsets		= msm8996_usb3phy_pcs_offsets,
> +
> +	.phy_init_serdes_tbl	= usb3phy_serdes_init_tbl,
> +	.phy_init_serdes_tbl_sz	= ARRAY_SIZE(usb3phy_serdes_init_tbl),
> +	.phy_init_tx_tbl	= usb3phy_tx_init_tbl,
> +	.phy_init_tx_tbl_sz	= ARRAY_SIZE(usb3phy_tx_init_tbl),
> +	.phy_init_rx_tbl	= usb3phy_rx_init_tbl,
> +	.phy_init_rx_tbl_sz	= ARRAY_SIZE(usb3phy_rx_init_tbl),
> +	.phy_init_pcs_tbl	= usb3phy_pcs_init_tbl,
> +	.phy_init_pcs_tbl_sz	= ARRAY_SIZE(usb3phy_pcs_init_tbl),
> +	.regs			= usb3phy_regs_layout,
> +	.mask_start_ctrl	= (PHY_SERDES_START | PHY_PCS_START),
> +	.mask_pwr_dn_ctrl	= PHY_SW_PWRDN,
> +};
> +
> +static void qcom_qmp_phy_configure(void __iomem *base,
> +				unsigned int *regs_layout,
> +				struct qmp_phy_init_tbl init_tbl[],
> +				int init_tbl_sz)
> +{
> +	int i;
> +
> +	for (i = 0; i < init_tbl_sz; i++) {
> +		if (init_tbl[i].in_layout)
> +			writel_relaxed(init_tbl[i].cfg_val,
> +				base + regs_layout[init_tbl[i].reg_offset]);
> +		else
> +			writel_relaxed(init_tbl[i].cfg_val,
> +				base + init_tbl[i].reg_offset);
> +	}
> +
> +	/* flush buffered writes */
> +	mb();
> +}
> +
> +static int qcom_qmp_phy_poweron(struct phy *phy)
> +{
> +	struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
> +	struct qcom_qmp_phy *qphy = phydesc->qphy;
> +	int ret;
> +
> +	dev_info(&phy->dev, "Powering on QMP phy\n");
> +
> +	ret = regulator_enable(qphy->vdda_phy);
> +	if (ret) {
> +		dev_err(qphy->dev, "%s: vdda-phy enable failed, err=%d\n",
> +				__func__, ret);
> +		return ret;
> +	}
> +
> +	ret = regulator_enable(qphy->vdda_pll);
> +	if (ret) {
> +		dev_err(qphy->dev, "%s: vdda-pll enable failed, err=%d\n",
> +				__func__, ret);
> +		goto err_vdda_pll;
> +	}
> +
> +	if (qphy->vddp_ref_clk) {
> +		ret = regulator_enable(qphy->vddp_ref_clk);
> +		if (ret) {
> +			dev_err(qphy->dev, "%s: vdda-ref-clk enable failed, err=%d\n",
> +					__func__, ret);
> +			goto err_vddp_refclk;
> +		}
> +	}
> +
> +	if (!qphy->clk_enabled) {
> +		clk_prepare_enable(qphy->ref_clk_src);
> +		clk_prepare_enable(qphy->ref_clk);
> +		clk_prepare_enable(phydesc->pipe_clk);
> +		qphy->clk_enabled = true;
> +	}
> +
> +	return 0;
> +
> +err_vddp_refclk:
> +	regulator_disable(qphy->vdda_pll);
> +err_vdda_pll:
> +	regulator_disable(qphy->vdda_phy);
> +	return ret;
> +}
> +
> +static int qcom_qmp_phy_poweroff(struct phy *phy)
> +{
> +	struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
> +	struct qcom_qmp_phy *qphy = phydesc->qphy;
> +
> +	if (qphy->clk_enabled) {
> +		clk_disable_unprepare(qphy->ref_clk_src);
> +		clk_disable_unprepare(qphy->ref_clk);
> +		clk_disable_unprepare(phydesc->pipe_clk);
> +		qphy->clk_enabled = false;
> +	}
> +
> +	if (qphy->vddp_ref_clk)
> +		regulator_disable(qphy->vddp_ref_clk);
> +
> +	regulator_disable(qphy->vdda_pll);
> +	regulator_disable(qphy->vdda_phy);
> +
> +	return 0;
> +}
> +
> +static int qcom_qmp_phy_is_ready(struct qcom_qmp_phy *qphy,
> +				void __iomem *pcs_status, u32 mask)
> +{
> +	unsigned int init_timeout;
> +
> +	init_timeout = PHY_READY_TIMEOUT_COUNT;
> +	do {
> +		if (readl_relaxed(pcs_status) & mask)
> +			break;
> +
> +		usleep_range(REFCLK_STABILIZATION_DELAY_US_MIN,
> +				 REFCLK_STABILIZATION_DELAY_US_MAX);
> +	} while (--init_timeout);
> +
> +	if (!init_timeout)
> +		return -EBUSY;
> +
> +	return 0;
> +}
> +
> +static int qcom_qmp_phy_com_init(struct qcom_qmp_phy *qphy)
> +{
> +	const struct qmp_phy_init_cfg *cfg = qphy->cfg;
> +	void __iomem *serdes = qphy->serdes;
> +	int ret;
> +
> +	mutex_lock(&qphy->phy_mutex);
> +	if (qphy->init_count++) {
> +		mutex_unlock(&qphy->phy_mutex);
> +		return 0;
> +	}
> +
> +	ret = reset_control_deassert(qphy->phy_rst);
> +	if (ret) {
> +		dev_err(qphy->dev, "phy reset deassert failed\n");
mutex unlock
> +		return ret;
> +	}
> +
> +	ret = reset_control_deassert(qphy->phycom_rst);
> +	if (ret) {
> +		dev_err(qphy->dev, "common reset deassert failed\n");
> +		goto err_phycom_rst;
> +	}
> +
> +	if (qphy->phycfg_rst) {
> +		ret = reset_control_deassert(qphy->phycfg_rst);
> +		if (ret) {
> +			dev_err(qphy->dev, "common reset deassert failed\n");
> +			goto err_phycfg_rst;
> +		}
> +	}
> +
> +	if (cfg->has_phy_com_ctrl) {
> +		qphy_setbits(PHY_SW_PWRDN,
> +			serdes + cfg->regs[QPHY_COM_POWER_DOWN_CONTROL]);
> +		/* Make sure that above write is completed */
> +		mb();
> +	}
> +
> +	/* Serdes configuration */
> +	qcom_qmp_phy_configure(serdes, cfg->regs, cfg->phy_init_serdes_tbl,
> +				cfg->phy_init_serdes_tbl_sz);
> +
> +	if (cfg->has_phy_com_ctrl) {
> +		qphy_clrbits(PHY_SW_RESET, serdes +
> +					cfg->regs[QPHY_COM_SW_RESET]);
> +		qphy_setbits(PHY_SERDES_START | PHY_PCS_START,
> +				serdes + cfg->regs[QPHY_COM_START_CONTROL]);
> +		/* Make sure that above write is completed */
> +		mb();
> +
> +		ret = qcom_qmp_phy_is_ready(qphy, serdes +
> +					cfg->regs[QPHY_COM_PCS_READY_STATUS],
> +					MASK_COM_PCS_READY);
> +		if (ret) {
> +			dev_err(qphy->dev,
> +				"common control block init timed-out\n");
> +			goto err_phy_comctrl;
> +		}
> +	}
> +
> +	mutex_unlock(&qphy->phy_mutex);
> +
> +	return 0;
> +
> +err_phy_comctrl:
mutex unlock
> +	if (qphy->phycfg_rst)
> +		reset_control_assert(qphy->phycfg_rst);
> +err_phycfg_rst:
> +	reset_control_assert(qphy->phycom_rst);
> +err_phycom_rst:
> +	reset_control_assert(qphy->phy_rst);
> +	return ret;
> +}
> +
> +static int qcom_qmp_phy_com_exit(struct qcom_qmp_phy *qphy)
> +{
> +	const struct qmp_phy_init_cfg *cfg = qphy->cfg;
> +	void __iomem *serdes = qphy->serdes;
> +
> +	mutex_lock(&qphy->phy_mutex);
> +	if (--qphy->init_count) {
> +		mutex_unlock(&qphy->phy_mutex);
> +		return 0;
> +	}
> +
> +	if (cfg->has_phy_com_ctrl) {
> +		qphy_setbits(PHY_SERDES_START | PHY_PCS_START,
> +				serdes + cfg->regs[QPHY_COM_START_CONTROL]);
> +		qphy_clrbits(PHY_SW_RESET, serdes +
> +					cfg->regs[QPHY_COM_SW_RESET]);
> +		qphy_setbits(PHY_SW_PWRDN,
> +			serdes + cfg->regs[QPHY_COM_POWER_DOWN_CONTROL]);
> +
> +		/* Make sure that above writes are completed */
> +		mb();
> +	}
> +
> +	reset_control_assert(qphy->phy_rst);
> +	reset_control_assert(qphy->phycom_rst);
> +	if (qphy->phycfg_rst)
> +		reset_control_assert(qphy->phycfg_rst);
> +
> +	mutex_unlock(&qphy->phy_mutex);
> +
> +	return 0;
> +}
> +
> +/* PHY Initialization */
> +static int qcom_qmp_phy_init(struct phy *phy)
> +{
> +	struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
> +	struct qcom_qmp_phy *qphy = phydesc->qphy;
> +	const struct qmp_phy_init_cfg *cfg = qphy->cfg;
> +	void __iomem *tx = phydesc->tx;
> +	void __iomem *rx = phydesc->rx;
> +	void __iomem *pcs = phydesc->pcs;
> +	int ret;
> +
> +	dev_info(qphy->dev, "Initializing QMP phy\n");
> +
> +	/* enable interface clocks to program phy */
> +	clk_prepare_enable(qphy->aux_clk);
> +	clk_prepare_enable(qphy->cfg_ahb_clk);
> +
> +	ret = qcom_qmp_phy_com_init(qphy);
> +	if (ret)
> +		goto err;
> +
> +	if (phydesc->lane_rst) {
> +		ret = reset_control_deassert(phydesc->lane_rst);
> +		if (ret) {
> +			dev_err(qphy->dev, "lane<%d> reset deassert failed\n",
> +					phydesc->index);
> +			goto err_lane_rst;
> +		}
> +	}
> +
> +	/* Tx, Rx, and PCS configurations */
> +	qcom_qmp_phy_configure(tx, cfg->regs, cfg->phy_init_tx_tbl,
> +				cfg->phy_init_tx_tbl_sz);
> +	qcom_qmp_phy_configure(rx, cfg->regs, cfg->phy_init_rx_tbl,
> +				cfg->phy_init_rx_tbl_sz);
> +	qcom_qmp_phy_configure(pcs, cfg->regs, cfg->phy_init_pcs_tbl,
> +				cfg->phy_init_pcs_tbl_sz);
> +
> +	/*
> +	 * Pull out PHY from POWER DOWN state:
> +	 * This is active low enable signal to power-down PHY.
> +	 */
> +	qphy_setbits(cfg->mask_pwr_dn_ctrl,
> +			pcs + QPHY_POWER_DOWN_CONTROL);
> +	/* XXX: 10 us delay; given in PCIE HPG only */
> +	usleep_range(POWER_DOWN_DELAY_US_MIN, POWER_DOWN_DELAY_US_MAX);
> +
> +	/* start SerDes and Phy-Coding-Sublayer */
> +	qphy_setbits(cfg->mask_start_ctrl, pcs + QPHY_START_CTRL);
> +
> +	/* Pull PHY out of reset state */
> +	qphy_clrbits(PHY_SW_RESET, pcs + QPHY_SW_RESET);
> +	/* Make sure that above writes are completed */
> +	mb();
> +
> +	ret = qcom_qmp_phy_is_ready(qphy, pcs +
> +					cfg->regs[QPHY_PCS_READY_STATUS],
> +					MASK_PHYSTATUS);
> +	if (ret) {
> +		dev_err(qphy->dev, "phy initialization timed-out\n");
> +		goto err_pcs_ready;
> +	}
> +
> +	return 0;
> +
> +err_pcs_ready:
> +	if (phydesc->lane_rst)
> +		reset_control_assert(phydesc->lane_rst);
> +err_lane_rst:
> +	qcom_qmp_phy_com_exit(qphy);
> +err:
> +	clk_disable_unprepare(qphy->cfg_ahb_clk);
> +	clk_disable_unprepare(qphy->aux_clk);
> +	return ret;
> +}
> +
> +static int qcom_qmp_phy_exit(struct phy *phy)
> +{
> +	struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
> +	struct qcom_qmp_phy *qphy = phydesc->qphy;
> +	const struct qmp_phy_init_cfg *cfg = qphy->cfg;
> +
> +	/* PHY reset */
> +	qphy_setbits(PHY_SW_RESET, phydesc->pcs + QPHY_SW_RESET);
> +
> +	/* stop SerDes and Phy-Coding-Sublayer */
> +	qphy_clrbits(cfg->mask_start_ctrl, phydesc->pcs + QPHY_START_CTRL);
> +
> +	/* Put PHY into POWER DOWN state: active low */
> +	qphy_clrbits(cfg->mask_pwr_dn_ctrl,
> +			phydesc->pcs + QPHY_POWER_DOWN_CONTROL);
> +
> +	/* Make sure that above writes are completed */
> +	mb();
> +
> +	if (phydesc->lane_rst)
> +		reset_control_assert(phydesc->lane_rst);
> +
> +	qcom_qmp_phy_com_exit(qphy);
> +
> +	clk_disable_unprepare(qphy->aux_clk);
> +	clk_disable_unprepare(qphy->cfg_ahb_clk);
> +
> +	return 0;
> +}
> +
> +
> +static int qcom_qmp_phy_regulator_init(struct device *dev)
> +{
> +	struct qcom_qmp_phy *qphy = dev_get_drvdata(dev);
> +	int ret = 0;
> +
> +	qphy->vdda_phy = devm_regulator_get(dev, "vdda-phy");
> +	if (IS_ERR(qphy->vdda_phy)) {
> +		ret = PTR_ERR(qphy->vdda_phy);
> +		dev_err(dev, "failed to get vdda-phy, %d\n", ret);
> +		return ret;
> +	}
> +
> +	qphy->vdda_pll = devm_regulator_get(dev, "vdda-pll");
> +	if (IS_ERR(qphy->vdda_pll)) {
> +		ret = PTR_ERR(qphy->vdda_pll);
> +		dev_err(dev, "failed to get vdda-pll, %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* optional regulator */
> +	qphy->vddp_ref_clk = devm_regulator_get(dev, "vddp-ref-clk");

Should mention this in bindings too.

> +	if (IS_ERR(qphy->vddp_ref_clk)) {
> +		ret = PTR_ERR(qphy->vddp_ref_clk);
> +		dev_info(dev, "failed to get vddp-ref-clk, %d\n", ret);
> +		qphy->vddp_ref_clk = NULL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int qcom_qmp_phy_clk_init(struct device *dev)
> +{
> +	struct qcom_qmp_phy *qphy = dev_get_drvdata(dev);
> +	int ret;
> +
> +	qphy->aux_clk = devm_clk_get(dev, "aux");
> +	if (IS_ERR(qphy->aux_clk)) {
> +		ret = PTR_ERR(qphy->aux_clk);
> +		if (ret != -EPROBE_DEFER)
> +			dev_err(dev, "failed to get aux_clk\n");
> +		return ret;
> +	}
> +
> +	qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb");
> +	if (IS_ERR(qphy->cfg_ahb_clk)) {
> +		ret = PTR_ERR(qphy->cfg_ahb_clk);
> +		if (ret != -EPROBE_DEFER)
> +			dev_err(dev, "failed to get cfg_ahb_clk\n");
> +		return ret;
> +	}
> +
> +	/*
> +	 * ref_clk and ref_clk_src handles may not be available in
> +	 * all hardwares. So we don't return error in these cases.
> +	 */

This thing has to be mentioned in the bindings too.

> +	qphy->ref_clk_src = devm_clk_get(dev, "ref_clk_src");
> +	if (IS_ERR(qphy->ref_clk_src)) {
> +		ret = PTR_ERR(qphy->ref_clk_src);
> +		if (ret != -EPROBE_DEFER) {
> +			qphy->ref_clk_src = NULL;
> +			dev_dbg(dev, "failed to get ref_clk_src\n");
> +		} else {
> +			return ret;
> +		}
> +	}
> +
> +	qphy->ref_clk = devm_clk_get(dev, "ref_clk");
> +	if (IS_ERR(qphy->ref_clk)) {
> +		ret = PTR_ERR(qphy->ref_clk);
> +		if (ret != -EPROBE_DEFER) {
> +			qphy->ref_clk = NULL;
> +			dev_dbg(dev, "failed to get ref_clk\n");
> +		} else {
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static struct phy *qcom_qmp_phy_xlate(struct device *dev,
> +					struct of_phandle_args *args)
> +{
> +	struct qcom_qmp_phy *qphy = dev_get_drvdata(dev);
> +	int i;
> +
> +	if (WARN_ON(args->args[0] >= qphy->cfg->nlanes))
> +		return ERR_PTR(-ENODEV);
> +
> +	for (i = 0; i < qphy->cfg->nlanes; i++) {
> +		if (qphy->phys[i]->index == args->args[0])
> +			break;
> +	}
> +
> +	if (i == qphy->cfg->nlanes)
> +		return ERR_PTR(-ENODEV);
> +
> +	return qphy->phys[i]->phy;
> +}
> +
> +static const struct phy_ops qcom_qmp_phy_gen_ops = {
> +	.init		= qcom_qmp_phy_init,
> +	.exit		= qcom_qmp_phy_exit,
> +	.power_on	= qcom_qmp_phy_poweron,
> +	.power_off	= qcom_qmp_phy_poweroff,
> +	.owner		= THIS_MODULE,
> +};
> +
> +static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
> +	{
> +		.compatible = "qcom,msm8996-qmp-pcie-phy",
> +		.data = &pciephy_init_cfg,
> +	}, {
> +		.compatible = "qcom,msm8996-qmp-usb3-phy",
> +		.data = &usb3phy_init_cfg,
> +	},
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, qcom_qmp_phy_of_match_table);
> +
> +static int qcom_qmp_phy_probe(struct platform_device *pdev)
> +{
> +	struct qcom_qmp_phy *qphy;
> +	struct device *dev = &pdev->dev;
> +	struct device_node *child;
> +	struct phy_provider *phy_provider;
> +	struct resource *res;
> +	const struct of_device_id *match;
> +	void __iomem *base;
> +	int ret = 0;
> +	int id;
> +
> +	qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
> +	if (!qphy)
> +		return -ENOMEM;
new line
> +	qphy->dev = dev;
> +	dev_set_drvdata(dev, qphy);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res)
> +		return -ENODEV;
new line
> +	base = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(base))
> +		return PTR_ERR(base);
> +
> +	/* per PHY serdes; usually located at base address */
> +	qphy->serdes = base;
> +
> +	mutex_init(&qphy->phy_mutex);
> +
> +	/* Get the specific init parameters of QMP phy */
> +	match = of_match_node(qcom_qmp_phy_of_match_table, dev->of_node);
> +	qphy->cfg = match->data;
> +
> +	ret = qcom_qmp_phy_clk_init(dev);
> +	if (ret) {
> +		dev_err(dev, "clock init failed\n");
> +		return ret;
> +	}
> +
> +	ret = qcom_qmp_phy_regulator_init(dev);
> +	if (ret) {
> +		dev_err(dev, "regulator init failed\n");
> +		return ret;
> +	}
> +
> +	qphy->phy_rst = devm_reset_control_get(dev, "phy");
> +	if (IS_ERR(qphy->phy_rst)) {
> +		dev_err(dev, "failed to get phy core reset\n");
> +		return PTR_ERR(qphy->phy_rst);
> +	}
> +
> +	qphy->phycom_rst = devm_reset_control_get(dev, "common");
> +	if (IS_ERR(qphy->phycom_rst)) {
> +		dev_err(dev, "failed to get phy common reset\n");
> +		return PTR_ERR(qphy->phycom_rst);
> +	}
> +
> +	qphy->phycfg_rst = devm_reset_control_get(dev, "cfg");
> +	if (IS_ERR(qphy->phycfg_rst)) {
> +		dev_err(dev, "failed to get phy ahb cfg reset\n");
> +		qphy->phycfg_rst = NULL;
> +	}
> +
> +	qphy->phys = devm_kcalloc(dev, qphy->cfg->nlanes,
> +					sizeof(*qphy->phys), GFP_KERNEL);
> +	if (!qphy->phys)
> +		return -ENOMEM;
> +
> +	for (id = 0; id < qphy->cfg->nlanes; id++) {
> +		struct phy *generic_phy;
> +		struct qmp_phy_desc *phy_desc;
> +		char prop_name[MAX_PROP_NAME];
> +
> +		phy_desc = devm_kzalloc(dev, sizeof(*phy_desc), GFP_KERNEL);
> +		if (!phy_desc) {
> +			ret = -ENOMEM;
> +			goto put_child;
> +		}
> +
> +		phy_desc->tx = base + qphy->cfg->tx_offsets[id];
> +		phy_desc->rx = base + qphy->cfg->rx_offsets[id];
> +		phy_desc->pcs = base + qphy->cfg->pcs_offsets[id];
> +
> +		/*
> +		 * Get PHY's Pipe clock, if any; USB3 and PCIe are PIPE
> +		 * based phys, so they essentially have pipe clock
> +		 */
> +		memset(&prop_name, 0, sizeof(prop_name));
> +		snprintf(prop_name, MAX_PROP_NAME, "pipe%d", id);
> +		phy_desc->pipe_clk = devm_clk_get(dev, prop_name);
> +		if (IS_ERR(phy_desc->pipe_clk)) {
> +			if (qphy->cfg->type == PHY_TYPE_PCIE ||
> +			    qphy->cfg->type == PHY_TYPE_USB3) {
> +				ret = PTR_ERR(phy_desc->pipe_clk);
> +				if (ret != -EPROBE_DEFER)
> +					dev_err(dev,
> +					"failed to get lane%d pipe_clk\n", id);
> +				return ret;
> +			} else {
> +				phy_desc->pipe_clk = NULL;
> +			}
> +		}

Do we need to really need phy type for this?
You should proably make a flag in cfg to mark this rather then using phy 
type. Hopefully it will also simplify the logic.


> +
> +		/* Get lane reset, if any */
> +		memset(&prop_name, 0, sizeof(prop_name));
> +		snprintf(prop_name, MAX_PROP_NAME, "lane%d", id);
> +		phy_desc->lane_rst = devm_reset_control_get(dev, prop_name);
> +		if (IS_ERR(phy_desc->lane_rst)) {
> +			if (qphy->cfg->type == PHY_TYPE_PCIE) {
> +				dev_err(dev, "failed to get lane%d reset\n",
> +									id);
> +				ret = PTR_ERR(phy_desc->lane_rst);
> +				goto put_child;
> +			} else {
> +				phy_desc->lane_rst = NULL;
> +			}
> +		}

Same comment as above.


> +
> +		generic_phy = devm_phy_create(dev, NULL, &qcom_qmp_phy_gen_ops);
> +		if (IS_ERR(generic_phy)) {
> +			ret = PTR_ERR(generic_phy);
> +			dev_err(dev, "failed to create qphy %d\n", ret);
> +			goto put_child;
> +		}
> +
> +		phy_desc->phy = generic_phy;
> +		phy_desc->index = id;
> +		phy_desc->qphy = qphy;
> +		phy_set_drvdata(generic_phy, phy_desc);
> +		qphy->phys[id] = phy_desc;
> +	}
> +
> +	phy_provider = devm_of_phy_provider_register(dev, qcom_qmp_phy_xlate);
> +	if (IS_ERR(phy_provider)) {
> +		ret = PTR_ERR(phy_provider);
> +		dev_err(dev, "failed to register qphy %d\n", ret);
> +		goto put_child;
> +	}
> +
> +put_child:
> +	of_node_put(child);
> +	return ret;
> +}
> +
> +static struct platform_driver qcom_qmp_phy_driver = {
> +	.probe		= qcom_qmp_phy_probe,
> +	.driver = {
> +		.name	= "qcom_qmp_phy",
> +		.of_match_table = of_match_ptr(qcom_qmp_phy_of_match_table),
> +	},
> +};
> +
> +module_platform_driver(qcom_qmp_phy_driver);
> +
> +MODULE_AUTHOR("Vivek Gautam <vivek.gautam-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>");
> +MODULE_DESCRIPTION("Qualcomm QMP PHY driver");
> +MODULE_LICENSE("GPL v2");
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] phy: qcom-qusb2: New driver for QUSB2 PHY on Qcom chips
  2016-10-19 10:43 ` [PATCH 1/2] phy: qcom-qusb2: New driver for QUSB2 PHY on Qcom chips Vivek Gautam
  2016-10-20  4:13   ` Vivek Gautam
@ 2016-10-26 19:56   ` Kishon Vijay Abraham I
  2016-11-02  8:59     ` Vivek Gautam
  2016-10-26 21:16   ` Rob Herring
  2 siblings, 1 reply; 19+ messages in thread
From: Kishon Vijay Abraham I @ 2016-10-26 19:56 UTC (permalink / raw)
  To: Vivek Gautam, robh+dt, mark.rutland
  Cc: devicetree, linux-kernel, linux-arm-msm

Hi,

On Wednesday 19 October 2016 04:13 PM, Vivek Gautam wrote:
> PHY transceiver driver for QUSB2 phy controller that provides
> HighSpeed functionality for DWC3 controller present on
> Qualcomm chipsets.
> 
> This driver is based on phy-msm-qusb driver available in
> msm-4.4 kernel @codeaurora[1]
> 
> [1] https://source.codeaurora.org/quic/la/kernel/msm-4.4/log/?h=caf/3.18/msm-3.18

I'd prefer only LKML links in commit msg. This can be part of the comment
section below ---.
> 
> Signed-off-by: Vivek Gautam <vivek.gautam@codeaurora.org>
> Cc: Kishon Vijay Abraham I <kishon@ti.com>
> ---
>  .../devicetree/bindings/phy/qcom-qusb2-phy.txt     |  37 ++
>  drivers/phy/Kconfig                                |  10 +
>  drivers/phy/Makefile                               |   1 +
>  drivers/phy/phy-qcom-qusb2.c                       | 577 +++++++++++++++++++++
>  4 files changed, 625 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt
>  create mode 100644 drivers/phy/phy-qcom-qusb2.c
> 
> diff --git a/Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt b/Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt
> new file mode 100644
> index 0000000..97c9ce7
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt
> @@ -0,0 +1,37 @@
> +Qualcomm QUSB2 phy controller
> +=============================
> +
> +QUSB2 controller supports LS/FS/HS usb connectivity on Qualcomm chipsets.
> +
> +Required properties:
> + - compatible: compatible list, contains "qcom,msm8996-qusb2-phy".
> + - reg: offset and length of the PHY register set.
> + - #phy-cells: must be 0.
> +
> + - clocks: a list of phandles and clock-specifier pairs,
> +	   one for each entry in clock-names.
> + - clock-names: must be "cfg_ahb" for phy config clock,
> +			"ref_clk" for 19.2 MHz ref clk,
> +			"ref_clk_src" reference clock source.
> +			"iface" for phy interface clock (Optional).
> +
> + - vdd-phy-supply: Phandle to a regulator supply to PHY core block.
> + - vdda-pll-supply: Phandle to 1.8V regulator supply to PHY refclk pll block.
> + - vdda-phy-dpdm: Phandle to 3.1V regulator supply to Dp/Dm port signals.
> +
> + - resets: a list of phandles and reset controller specifier pairs,
> +	   one for each entry in reset-names.
> + - reset-names: must be "phy" for reset of phy block.
> +
> +Optional properties:
> + - nvmem-cells: a list of phandles to nvmem cells that contain fused
> +		tuning parameters for qusb2 phy, one for each entry
> +		in nvmem-cell-names.
> + - nvmem-cell-names: must be "tune2_hstx_trim_efuse" for cell containing
> +		     HS Tx trim value.
> + - qcom,hstx-trim-bit-offset: bit offset within nvmem cell for
> +			      HS Tx trim value.
> + - qcom,hstx-trim-bit-len: bit length of HS Tx trim value within nvmem cell.
> +
> + - qcom,tcsr-syscon: Phandle to TCSR syscon register region.
> + - qcom,phy-clk-scheme: Offset to TCSR_PHY_CLK_SCHEME_SEL register.
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index fe00f91..5547984 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -446,6 +446,16 @@ config PHY_STIH41X_USB
>  	  Enable this to support the USB transceiver that is part of
>  	  STMicroelectronics STiH41x SoC series.
>  
> +config PHY_QCOM_QUSB2
> +	tristate "Qualcomm QUSB2 PHY Driver"
> +	depends on OF && (ARCH_QCOM || COMPILE_TEST)
> +	select GENERIC_PHY
> +	help
> +	  Enable this to support the HighSpeed QUSB2 PHY transceiver for USB
> +	  controllers on Qualcomm chips. This driver supports the high-speed
> +	  PHY which is usually paired with either the ChipIdea or Synopsys DWC3
> +	  USB IPs on MSM SOCs.
> +
>  config PHY_QCOM_UFS
>  	tristate "Qualcomm UFS PHY driver"
>  	depends on OF && ARCH_QCOM
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index a534cf5..848489d 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -51,6 +51,7 @@ obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+= phy-spear1340-miphy.o
>  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_QUSB2) 	+= phy-qcom-qusb2.o
>  obj-$(CONFIG_PHY_QCOM_UFS) 	+= phy-qcom-ufs.o
>  obj-$(CONFIG_PHY_QCOM_UFS) 	+= phy-qcom-ufs-qmp-20nm.o
>  obj-$(CONFIG_PHY_QCOM_UFS) 	+= phy-qcom-ufs-qmp-14nm.o
> diff --git a/drivers/phy/phy-qcom-qusb2.c b/drivers/phy/phy-qcom-qusb2.c
> new file mode 100644
> index 0000000..058f661
> --- /dev/null
> +++ b/drivers/phy/phy-qcom-qusb2.c
> @@ -0,0 +1,577 @@
> +/*
> + * Copyright (c) 2016, The Linux Foundation. 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/delay.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/of.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +#include <linux/slab.h>
> +
> +#define QUSB2PHY_PLL_TEST		0x04
> +#define CLK_REF_SEL			BIT(7)
> +
> +#define QUSB2PHY_PLL_TUNE		0x08
> +#define QUSB2PHY_PLL_USER_CTL1		0x0c
> +#define QUSB2PHY_PLL_USER_CTL2		0x10
> +#define QUSB2PHY_PLL_AUTOPGM_CTL1	0x1c
> +#define QUSB2PHY_PLL_PWR_CTRL		0x18
> +
> +#define QUSB2PHY_PLL_STATUS		0x38
> +#define PLL_LOCKED			BIT(5)
> +
> +#define QUSB2PHY_PORT_TUNE1             0x80
> +
> +#define QUSB2PHY_PORT_TUNE2             0x84
> +/* TUNE2's high nibble value read from efuse */
> +#define TUNE2_HIGH_NIBBLE_VAL(val, pos, mask)	((val >> pos) & mask)
> +
> +#define QUSB2PHY_PORT_TUNE3             0x88
> +#define QUSB2PHY_PORT_TUNE4             0x8C
> +#define QUSB2PHY_PORT_TUNE5		0x90tx
> +#define QUSB2PHY_PORT_TEST2		0x9c
> +
> +#define QUSB2PHY_PORT_POWERDOWN		0xB4
> +#define CLAMP_N_EN			BIT(5)
> +#define FREEZIO_N			BIT(1)
> +#define POWER_DOWN			BIT(0)
> +
> +#define QUSB2PHY_REFCLK_ENABLE		BIT(0)
> +
> +#define PHY_CLK_SCHEME_SEL		BIT(0)
> +
> +struct qusb2_phy_init_tbl {
> +	unsigned int reg_offset;
> +	unsigned int cfg_val;
> +};
> +#define QCOM_QUSB2_PHY_INIT_CFG(reg, val) \
> +	{				\
> +		.reg_offset = reg,	\
> +		.cfg_val = val,		\
> +	}
> +
> +static struct qusb2_phy_init_tbl msm8996_phy_init_tbl[] = {
> +	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE1, 0xF8),
> +	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE2, 0xB3),
> +	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE3, 0x83),
> +	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE4, 0xC0),
> +	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30),
> +	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79),
> +	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21),
> +	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TEST2, 0x14),
> +	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9F),
> +	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00),
> +};
we should generalize this so that it could be used for any phys. All these
tuning parameters can come from dt.

phy,tx-swing = <reg_offset> <swing value>;
phy,rx-swing = <reg_offset> <swing value>;
phy,tx-compensation = <reg_offset> <swing value>;
phy,rx-compensation = <reg_offset> <swing value>;
..

We have to identify all the calibration parameters and add a dt property for
each parameter. The phy-core can just use these properties to calibrate or tune
the PHY.

> +
> +struct qusb2_phy_init_cfg {
> +	struct qusb2_phy_init_tbl *phy_init_tbl;
> +	int phy_init_tbl_sz;
> +};
> +
> +const struct qusb2_phy_init_cfg msm8996_phy_init_cfg = {
> +	.phy_init_tbl = msm8996_phy_init_tbl,
> +	.phy_init_tbl_sz = ARRAY_SIZE(msm8996_phy_init_tbl),
> +};
> +
> +/**
> + * struct qusb2_phy: Structure holding qusb2 phy attributes.
> + *
> + * @phy: pointer to generic phy.
> + * @base: pointer to iomapped memory space for qubs2 phy.
> + *
> + * @cfg_ahb_clk: pointer to AHB2PHY interface clock.
> + * @ref_clk: pointer to reference clock.
> + * @ref_clk_src: pointer to source to reference clock.
> + * @iface_src: pointer to phy interface clock.
> + * @clk_enabled: check if clocks are enabled.

Why is this needed? IMO this should not be used. clk framework can handle this
better since it maintains a reference count for clk_enable/clk_disable.
> + *
> + * @phy_reset: Pointer to phy reset control
> + *
> + * @vdda_phy: vdd supply to the phy core block.
> + * @vdda_pll: 1.8V vdd supply to ref_clk block.
> + * @vdda_phy_dpdm: 3.1V vdd supply to Dp/Dm port signals.
> + * @pwr_enabled: check if the regulators are enabled.

same here.
> + * @tcsr: pointer to TCSR syscon register map.
> + * @clk_scheme_offset: offset to PHY_CLK_SCHEME register in TCSR map.
> + * @cfg: phy initialization config data
> + */
> +struct qusb2_phy {
> +	struct phy *phy;
> +	void __iomem *base;
> +
> +	struct clk *cfg_ahb_clk;
> +	struct clk *ref_clk;
> +	struct clk *ref_clk_src;
> +	struct clk *iface_clk;
> +	bool clk_enabled;
> +
> +	struct reset_control *phy_reset;
> +
> +	struct regulator *vdd_phy;
> +	struct regulator *vdda_pll;
> +	struct regulator *vdda_phy_dpdm;
> +	bool pwr_enabled;
> +
> +	struct regmap *tcsr;
> +	unsigned int clk_scheme_offset;
> +
> +	const struct qusb2_phy_init_cfg *cfg;
> +};
> +
> +static inline void qusb2_set_bits(void __iomem *reg, u32 mask)

mask is misleading. just use val.
> +{
> +	u32 val;
> +
> +	val = readl_relaxed(reg);
> +	val |= mask;
> +	writel_relaxed(val, reg);
> +
> +	/* Ensure above write is completed */
> +	mb();
> +}
> +
> +static inline void qusb2_clr_bits(void __iomem *reg, u32 mask)
> +{
> +	u32 val;
> +
> +	val = readl_relaxed(reg);
> +	val &= ~mask;
> +	writel_relaxed(val, reg);
> +
> +	/* Ensure above write is completed */
> +	mb();
> +}
> +
> +static void qcom_qusb2_phy_configure(void __iomem *base,
> +				struct qusb2_phy_init_tbl init_tbl[],
> +				int init_tbl_sz)
> +{
> +	int i;
> +
> +	for (i = 0; i < init_tbl_sz; i++) {
> +		writel_relaxed(init_tbl[i].cfg_val,
> +				base + init_tbl[i].reg_offset);
> +	}
> +
> +	/* flush buffered writes */
> +	mb();
> +}
> +
> +static void qusb2_phy_enable_clocks(struct qusb2_phy *qphy, bool on)
> +{
> +	dev_dbg(&qphy->phy->dev, "%s(): clocks_enabled:%d on:%d\n",
> +			__func__, qphy->clk_enabled, on);

unnecessary debug print IMO, given that there is another debug print before the
function returns.
> +
> +	if (!qphy->clk_enabled && on) {
> +		clk_prepare_enable(qphy->cfg_ahb_clk);
> +		if (qphy->iface_clk)
> +			clk_prepare_enable(qphy->iface_clk);
> +		clk_prepare_enable(qphy->ref_clk_src);
> +		qphy->clk_enabled = true;
> +	}
> +
> +	if (qphy->clk_enabled && !on) {
> +		clk_disable_unprepare(qphy->ref_clk_src);
> +		if (qphy->iface_clk)
> +			clk_disable_unprepare(qphy->iface_clk);
> +		clk_disable_unprepare(qphy->cfg_ahb_clk);
> +		qphy->clk_enabled = false;
> +	}
> +
> +	dev_dbg(&qphy->phy->dev, "%s(): clocks_enabled:%d\n", __func__,
> +						qphy->clk_enabled);

even this should be dev_vdbg.
> +}
> +
> +static int qusb2_phy_enable_power(struct qusb2_phy *qphy, bool on)
> +{
> +	int ret = 0;
> +	struct device *dev = &qphy->phy->dev;
> +
> +	dev_dbg(dev, "%s(): turn %s regulators. power_enabled:%d\n",
> +			__func__, on ? "on" : "off", qphy->pwr_enabled);
> +
> +	if (qphy->pwr_enabled == on)
> +		return 0;
> +
> +	if (!on)
> +		goto disable_vdda_phy_dpdm;
> +
> +	ret = regulator_enable(qphy->vdd_phy);
> +	if (ret) {
> +		dev_err(dev, "Unable to enable vdd-phy:%d\n", ret);
> +		goto err_vdd_phy;
> +	}
> +
> +	ret = regulator_enable(qphy->vdda_pll);
> +	if (ret) {
> +		dev_err(dev, "Unable to enable vdda-pll:%d\n", ret);
> +		goto disable_vdd_phy;
> +	}
> +
> +	ret = regulator_enable(qphy->vdda_phy_dpdm);
> +	if (ret) {
> +		dev_err(dev, "Unable to enable vdda-phy-dpdm:%d\n", ret);
> +		goto disable_vdda_pll;
> +	}
> +
> +	qphy->pwr_enabled = true;
> +	dev_dbg(dev, "%s() regulators are turned on.\n", __func__);
> +
> +	return ret;
> +
> +disable_vdda_phy_dpdm:
> +	regulator_disable(qphy->vdda_phy_dpdm);
> +disable_vdda_pll:
> +	regulator_disable(qphy->vdda_pll);
> +disable_vdd_phy:
> +	regulator_disable(qphy->vdd_phy);
> +err_vdd_phy:
> +	qphy->pwr_enabled = false;
> +	dev_dbg(dev, "%s() regulators are turned off.\n", __func__);
> +	return ret;
> +}
> +
> +/*
> + * Fetches HS Tx tuning value from e-fuse and sets QUSB2PHY_PORT_TUNE2
> + * register.
> + * For any error case, skip setting the value and use the default value.
> + */
> +static int qusb2_phy_set_tune2_param(struct qusb2_phy *qphy)
> +{
> +	struct device *dev = &qphy->phy->dev;
> +	struct nvmem_cell *cell;
> +	unsigned int bit_mask;
> +	u32 tune2_val;
> +	int bit_offset = 0;
> +	int bit_len = 0;
> +	ssize_t len;
> +	u32 *val;
> +	int ret;
> +
> +	/*
> +	 * Read EFUSE register having TUNE2 parameter's high nibble.
> +	 * If efuse register shows value as 0x0, or if we fail to find
> +	 * a valid efuse register settings, then use default value
> +	 * as 0xB for high nibble that we have already set while
> +	 * configuring phy.
> +	 */
> +	cell = devm_nvmem_cell_get(dev, "tune2_hstx_trim_efuse");
> +	if (IS_ERR(cell)) {
> +		if (PTR_ERR(cell) == -EPROBE_DEFER)
> +			return PTR_ERR(cell);
> +		goto skip;
> +	}
> +
> +	val = (u32 *)nvmem_cell_read(cell, &len);
> +	if (!IS_ERR(val)) {
> +		ret = of_property_read_u32(dev->of_node,
> +						"qcom,hstx-trim-bit-offset",
> +						&bit_offset);
> +		if (ret) {
> +			dev_dbg(dev, "hstx-trim bit offset is invalid.\n");
> +			goto skip;
> +		}
> +
> +		ret = of_property_read_u32(dev->of_node,
> +						"qcom,hstx-trim-bit-len",
> +						&bit_len);
> +		if (ret) {
> +			dev_dbg(dev, "hstx-trim bit length is invalid.\n");
> +			goto skip;
> +		}
> +
> +		bit_mask = (1 << bit_len) - 1;
> +		dev_dbg(dev,
> +			"%s(): efuse value:%x, bit_mask:%x, bit_offset: %d\n",
> +			__func__, val[0], bit_mask, bit_offset);
> +		tune2_val = TUNE2_HIGH_NIBBLE_VAL(val[0], bit_offset, bit_mask);
> +		if (!tune2_val) {
> +			dev_dbg(dev, "hstx-trim bit length is invalid.\n");
> +			goto skip;
> +		}
> +
> +		/* Fused TUNE2 value is the higher nibble only */
> +		tune2_val = tune2_val << 0x4;
> +		qusb2_set_bits(qphy->base + QUSB2PHY_PORT_TUNE2, tune2_val);
> +	} else {
> +		dev_dbg(dev, "failed reading hs-tx trim value: %d\n", ret);
> +	}
> +
> +skip:
> +	return 0;
> +}
> +
> +static int qusb2_phy_init(struct phy *phy)
> +{
> +	struct qusb2_phy *qphy = phy_get_drvdata(phy);
> +	unsigned int reset_val;
> +	unsigned int clk_scheme;
> +	bool is_se_clk = false;
> +	int ret;
> +
> +	dev_info(&phy->dev, "Initializing QUSB2 phy\n");

dev_vdbg here..
> +
> +	ret = qusb2_phy_enable_power(qphy, true);
> +	if (ret)
> +		return ret;

shouldn't this be done in power_on?
> +
> +	qusb2_phy_enable_clocks(qphy, true);
> +
> +	/* Perform phy reset */
> +	ret = reset_control_assert(qphy->phy_reset);
> +	if (ret) {
> +		dev_err(&phy->dev, "Failed to assert phy_reset\n");
> +		return ret;
> +	}
> +	usleep_range(100, 150);

add a comment for the above delay.
> +	ret = reset_control_deassert(qphy->phy_reset);
> +	if (ret) {
> +		dev_err(&phy->dev, "Failed to de-assert phy_reset\n");
> +		return ret;
> +	}
> +
> +	/* Disable the PHY */
> +	qusb2_set_bits(qphy->base + QUSB2PHY_PORT_POWERDOWN,
> +			CLAMP_N_EN | FREEZIO_N | POWER_DOWN);
> +
> +	/* save reset value to override based on clk scheme */
> +	reset_val = readl_relaxed(qphy->base + QUSB2PHY_PLL_TEST);
> +
> +	qcom_qusb2_phy_configure(qphy->base, qphy->cfg->phy_init_tbl,
> +				qphy->cfg->phy_init_tbl_sz);
> +
> +	/* Check for efuse value for tuning the PHY */
> +	ret = qusb2_phy_set_tune2_param(qphy);
> +	if (ret)
> +		return ret;
> +
> +	/* Enable the PHY */
> +	qusb2_clr_bits(qphy->base + QUSB2PHY_PORT_POWERDOWN, POWER_DOWN);
> +
> +	/* Require to get phy pll lock successfully */
> +	usleep_range(150, 160);
> +
> +	/*
> +	 * Use differential clk by default; later we read TCSR_PHY_CLK_SCHEME
> +	 * register to check if Single-ended clock scheme is selected. If yes,
> +	 * then disable differential ref_clk and use single-ended clock.
> +	 */
> +
> +	if (qphy->tcsr) {
> +		ret = regmap_read(qphy->tcsr, qphy->clk_scheme_offset,
> +							&clk_scheme);
> +		/* is SE-clk available ? */
> +		if (clk_scheme & PHY_CLK_SCHEME_SEL) {
> +			dev_dbg(&phy->dev, "%s: select single-ended clk src\n",
> +								__func__);
> +			is_se_clk = true;
> +		}
> +	}
> +
> +	if (is_se_clk) {
> +		reset_val |= CLK_REF_SEL;
> +		clk_disable_unprepare(qphy->ref_clk);
> +	} else {
> +		reset_val &= ~CLK_REF_SEL;
> +		clk_prepare_enable(qphy->ref_clk);
> +	}
> +
> +	writel_relaxed(reset_val, qphy->base + QUSB2PHY_PLL_TEST);
> +
> +	/* Make sure that above write is completed to get PLL source clock */
> +	wmb();
> +
> +	/* Required to get PHY PLL lock successfully */
> +	usleep_range(100, 110);
> +
> +	if (!(readb_relaxed(qphy->base + QUSB2PHY_PLL_STATUS) &
> +					PLL_LOCKED)) {
> +		dev_err(&phy->dev, "QUSB PHY PLL LOCK fails:%x\n",
> +			readb_relaxed(qphy->base + QUSB2PHY_PLL_STATUS));
> +		return -EBUSY;
> +	}
> +
> +	return 0;
> +}
> +
> +static int qusb2_phy_exit(struct phy *phy)
> +{
> +	struct qusb2_phy *qphy = phy_get_drvdata(phy);
> +
> +	/* Disable the PHY */
> +	qusb2_set_bits(qphy->base + QUSB2PHY_PORT_POWERDOWN,
> +			CLAMP_N_EN | FREEZIO_N | POWER_DOWN);
> +
> +	qusb2_phy_enable_clocks(qphy, false);
> +	qusb2_phy_enable_power(qphy, false);
> +
> +	return 0;
> +}
> +
> +static const struct phy_ops qusb2_phy_gen_ops = {
> +	.init		= qusb2_phy_init,
> +	.exit		= qusb2_phy_exit,

you can add callbacks for power_on and power_off and add then enable clocks and
power here?

> +	.owner		= THIS_MODULE,
> +};
> +
> +static const struct of_device_id qusb2_phy_of_match_table[] = {
> +	{
> +		.compatible	= "qcom,msm8996-qusb2-phy",
> +		.data		= &msm8996_phy_init_cfg,
> +	},
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, qusb2_phy_of_match_table);
> +
> +static int qusb2_phy_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct qusb2_phy *qphy;
> +	struct phy_provider *phy_provider;
> +	struct phy *generic_phy;
> +	const struct of_device_id *match;
> +	struct resource *res;
> +	int ret = 0;
> +
> +	qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
> +	if (!qphy)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res)
> +		return -ENODEV;

the above check is not required. ioremap will take care.
> +	qphy->base = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(qphy->base))
> +		return PTR_ERR(qphy->base);
> +
> +	qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb_clk");
> +	if (IS_ERR(qphy->cfg_ahb_clk)) {
> +		ret = PTR_ERR(qphy->cfg_ahb_clk);
> +		if (ret != -EPROBE_DEFER)
> +			dev_err(dev, "failed to get cfg_ahb_clk\n");
> +		return ret;
> +	}
> +
> +	qphy->ref_clk_src = devm_clk_get(dev, "ref_clk_src");
> +	if (IS_ERR(qphy->ref_clk_src)) {
> +		qphy->ref_clk_src = NULL;
> +		ret = PTR_ERR(qphy->ref_clk_src);
> +		if (ret != -EPROBE_DEFER)
> +			dev_dbg(dev, "clk get failed for ref_clk_src\n");

don't we have to return on PROBE_DEFER?
> +	}
> +
> +	qphy->ref_clk = devm_clk_get(dev, "ref_clk");
> +	if (IS_ERR(qphy->ref_clk)) {
> +		qphy->ref_clk = NULL;
> +		ret = PTR_ERR(qphy->ref_clk);
> +		if (ret != -EPROBE_DEFER)
> +			dev_dbg(dev, "clk get failed for ref_clk\n");

same here.
> +	} else {
> +		clk_set_rate(qphy->ref_clk, 19200000);
> +	}
> +
> +	qphy->iface_clk = devm_clk_get(dev, "iface_clk");
> +	if (IS_ERR(qphy->iface_clk)) {
> +		qphy->iface_clk = NULL;
> +		ret = PTR_ERR(qphy->iface_clk);
> +		if (ret != -EPROBE_DEFER)
> +			dev_dbg(dev, "clk get failed for iface_clk\n");

here..
> +	}
> +
> +	qphy->phy_reset = devm_reset_control_get(&pdev->dev, "phy");
> +	if (IS_ERR(qphy->phy_reset)) {
> +		dev_err(dev, "failed to get phy core reset\n");
> +		return PTR_ERR(qphy->phy_reset);
> +	}
> +
> +	qphy->vdd_phy = devm_regulator_get(dev, "vdd-phy");
> +	if (IS_ERR(qphy->vdd_phy)) {
> +		dev_err(dev, "unable to get vdd-phy supply\n");
> +		return PTR_ERR(qphy->vdd_phy);
> +	}
> +
> +	qphy->vdda_pll = devm_regulator_get(dev, "vdda-pll");
> +	if (IS_ERR(qphy->vdda_pll)) {
> +		dev_err(dev, "unable to get vdda-pll supply\n");
> +		return PTR_ERR(qphy->vdda_pll);
> +	}
> +
> +	qphy->vdda_phy_dpdm = devm_regulator_get(dev, "vdda-phy-dpdm");
> +	if (IS_ERR(qphy->vdda_phy_dpdm)) {
> +		dev_err(dev, "unable to get vdda-phy-dpdm supply\n");
> +		return PTR_ERR(qphy->vdda_phy_dpdm);
> +	}
> +
> +	/* Get the specific init parameters of QMP phy */
> +	match = of_match_node(qusb2_phy_of_match_table, dev->of_node);
> +	qphy->cfg = match->data;
> +
> +	qphy->tcsr = syscon_regmap_lookup_by_phandle(dev->of_node,
> +							"qcom,tcsr-syscon");
> +	if (!IS_ERR(qphy->tcsr)) {
> +		ret = of_property_read_u32(dev->of_node, "qcom,phy-clk-scheme",
> +						&qphy->clk_scheme_offset);
> +		if (ret) {
> +			dev_err(dev, "phy-clk-scheme reg offset is invalid\n");
> +			qphy->tcsr = NULL;
> +			return ret;
> +		}
> +	} else {
> +		dev_dbg(dev, "Failed to lookup TCSR regmap\n");
> +		qphy->tcsr = NULL;
> +	}
> +
> +	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
> +	if (IS_ERR(phy_provider)) {
> +		ret = PTR_ERR(phy_provider);
> +		dev_err(dev, "%s: failed to register phy %d\n", __func__, ret);
> +		return ret;
> +	}
> +
> +	generic_phy = devm_phy_create(dev, NULL, &qusb2_phy_gen_ops);
> +	if (IS_ERR(generic_phy)) {
> +		ret = PTR_ERR(generic_phy);
> +		dev_err(dev, "%s: failed to create phy %d\n", __func__, ret);
> +		generic_phy = NULL;
> +		return ret;
> +	}

phy should be created before registering the PHY.
> +	qphy->phy = generic_phy;
> +
> +	dev_set_drvdata(dev, qphy);
> +	phy_set_drvdata(generic_phy, qphy);

this should be done before phy_provider_register.

Thanks
Kishon

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] phy: qcom-qmp: new qmp phy driver for qcom-chipsets
  2016-10-19 10:43 ` [PATCH 2/2] phy: qcom-qmp: new qmp phy driver for qcom-chipsets Vivek Gautam
  2016-10-19 10:54   ` Vivek Gautam
@ 2016-10-26 20:11   ` Kishon Vijay Abraham I
       [not found]     ` <ca4054a8-a813-0f50-3224-1e418eaa7095-l0cyMroinI0@public.gmane.org>
       [not found]   ` <1476873827-7191-3-git-send-email-vivek.gautam-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
  2 siblings, 1 reply; 19+ messages in thread
From: Kishon Vijay Abraham I @ 2016-10-26 20:11 UTC (permalink / raw)
  To: Vivek Gautam, robh+dt, mark.rutland
  Cc: devicetree, linux-kernel, linux-arm-msm

Hi,

On Wednesday 19 October 2016 04:13 PM, Vivek Gautam wrote:
> Qualcomm SOCs have QMP phy controller that provides support
> to a number of controller, viz. PCIe, UFS, and USB.
> Add a new driver, based on generic phy framework, for this
> phy controller.
> 
> USB3-phy changes: Based on phy-msm-ssusb-qmp driver available on
> msm-4.4 kernel @codeaurora[1].
> PCIe-phy changes: Based on msm8996-pcie-phy driver posted by
> Srinivas [2].
> 
> [1] https://source.codeaurora.org/quic/la/kernel/msm-4.4/log/?h=caf/3.18/msm-3.18
> [2] https://patchwork.kernel.org/patch/9318947/

use only lkml links here.
> 
> Signed-off-by: Vivek Gautam <vivek.gautam@codeaurora.org>
> Cc: Kishon Vijay Abraham I <kishon@ti.com>
> ---
>  .../devicetree/bindings/phy/qcom-qmp-phy.txt       |   61 ++
>  drivers/phy/Kconfig                                |    8 +
>  drivers/phy/Makefile                               |    1 +
>  drivers/phy/phy-qcom-qmp.c                         | 1154 ++++++++++++++++++++
>  4 files changed, 1224 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
>  create mode 100644 drivers/phy/phy-qcom-qmp.c
> 
> diff --git a/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
> new file mode 100644
> index 0000000..90214aa
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
> @@ -0,0 +1,61 @@
> +Qualcomm QMP PHY
> +----------------
> +
> +QMP phy controller supports physical layer functionality for a number of
> +controllers on Qualcomm chipsets, such as, PCIe, UFS, and USB.
> +
> +Required properties:
> + - compatible: compatible list, contains:
> +	       "qcom,msm8996-qmp-pcie-phy" for 14nm PCIe phy on msm8996,
> +	       "qcom,msm8996-qmp-usb3-phy" for 14nm USB3 phy on msm8996.
> + - reg: offset and length of the PHY register set.
> + - #phy-cells: must be 1
> +    - Cell after phy phandle should be the port (lane) number.
> + - clocks: a list of phandles and clock-specifier pairs,
> +	   one for each entry in clock-names.
> + - clock-names: must be "cfg_ahb" for phy config clock,
> +			"aux" for phy aux clock,
> +			"ref_clk" for 19.2 MHz ref clk,
> +			"ref_clk_src" for reference clock source,
> +			"pipe<port-number>" for pipe clock specific to
> +			each port/lane (Optional).
> + - resets: a list of phandles and reset controller specifier pairs,
> +	   one for each entry in reset-names.
> + - reset-names: must be "phy" for reset of phy block,
> +			"common" for phy common block reset,
> +			"cfg" for phy's ahb cfg block reset (Optional).
> +			"port<port-number>" for reset specific to
> +			each port/lane. (Optional)
> + - vdda-phy-supply: Phandle to a regulator supply to PHY core block.
> + - vdda-pll-supply: Phandle to 1.8V regulator supply to PHY refclk pll block.
> +
> +Optional properties:
> + - vddp-ref-clk-supply: Phandle to a regulator supply to any specific refclk
> +			pll block.
> +
> +Example:
> +	pcie_phy: pciephy@34000 {
> +		compatible = "qcom,qmp-14nm-pcie-phy";
> +		reg = <0x034000 0x3fff>;
> +		#phy-cells = <1>;
> +
> +		clocks = <&gcc GCC_PCIE_PHY_AUX_CLK>,
> +			<&gcc GCC_PCIE_PHY_CFG_AHB_CLK>,
> +			<&gcc GCC_PCIE_0_PIPE_CLK>,
> +			<&gcc GCC_PCIE_1_PIPE_CLK>,
> +			<&gcc GCC_PCIE_2_PIPE_CLK>;
> +		clock-names = "aux", "cfg_ahb",
> +				"pipe0", "pipe1", "pipe2";
> +
> +		vdda-phy-supply = <&pm8994_l28>;
> +		vdda-pll-supply = <&pm8994_l12>;
> +
> +		resets = <&gcc GCC_PCIE_PHY_BCR>,
> +			<&gcc GCC_PCIE_PHY_COM_BCR>,
> +			<&gcc GCC_PCIE_PHY_COM_NOCSR_BCR>,
> +			<&gcc GCC_PCIE_0_PHY_BCR>,
> +			<&gcc GCC_PCIE_1_PHY_BCR>,
> +			<&gcc GCC_PCIE_2_PHY_BCR>;
> +		reset-names = "phy", "common", "cfg",
> +				"lane0", "lane1", "lane2";
> +	};
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index 5547984..d5e2b50f 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -446,6 +446,14 @@ config PHY_STIH41X_USB
>  	  Enable this to support the USB transceiver that is part of
>  	  STMicroelectronics STiH41x SoC series.
>  
> +config PHY_QCOM_QMP
> +	tristate "Qualcomm QMP PHY Driver"
> +	depends on OF && (ARCH_QCOM || COMPILE_TEST)
> +	select GENERIC_PHY
> +	help
> +	  Enable this to support the QMP PHY transceiver that is used
> +	  with controllers such as PCIe, UFS, and USB on Qualcomm chips.
> +
>  config PHY_QCOM_QUSB2
>  	tristate "Qualcomm QUSB2 PHY Driver"
>  	depends on OF && (ARCH_QCOM || COMPILE_TEST)
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index 848489d..fde9fba 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -51,6 +51,7 @@ obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+= phy-spear1340-miphy.o
>  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_QMP) 	+= phy-qcom-qmp.o
>  obj-$(CONFIG_PHY_QCOM_QUSB2) 	+= phy-qcom-qusb2.o
>  obj-$(CONFIG_PHY_QCOM_UFS) 	+= phy-qcom-ufs.o
>  obj-$(CONFIG_PHY_QCOM_UFS) 	+= phy-qcom-ufs-qmp-20nm.o
> diff --git a/drivers/phy/phy-qcom-qmp.c b/drivers/phy/phy-qcom-qmp.c
> new file mode 100644
> index 0000000..7e89179
> --- /dev/null
> +++ b/drivers/phy/phy-qcom-qmp.c
> @@ -0,0 +1,1154 @@
> +/*
> + * Copyright (c) 2016, The Linux Foundation. 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/delay.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/reset.h>
> +#include <linux/slab.h>
> +
> +#include <dt-bindings/phy/phy.h>
> +
> +/* QMP PHY QSERDES COM registers */
> +#define QSERDES_COM_BG_TIMER				0x00c
> +#define QSERDES_COM_SSC_EN_CENTER			0x010
> +#define QSERDES_COM_SSC_ADJ_PER1			0x014
> +#define QSERDES_COM_SSC_ADJ_PER2			0x018
> +#define QSERDES_COM_SSC_PER1				0x01c
> +#define QSERDES_COM_SSC_PER2				0x020
> +#define QSERDES_COM_SSC_STEP_SIZE1			0x024
> +#define QSERDES_COM_SSC_STEP_SIZE2			0x028
> +#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN			0x034
> +#define QSERDES_COM_CLK_ENABLE1				0x038
> +#define QSERDES_COM_SYS_CLK_CTRL			0x03c
> +#define QSERDES_COM_SYSCLK_BUF_ENABLE			0x040
> +#define QSERDES_COM_PLL_IVCO				0x048
> +#define QSERDES_COM_LOCK_CMP1_MODE0			0x04c
> +#define QSERDES_COM_LOCK_CMP2_MODE0			0x050
> +#define QSERDES_COM_LOCK_CMP3_MODE0			0x054
> +#define QSERDES_COM_LOCK_CMP1_MODE1			0x058
> +#define QSERDES_COM_LOCK_CMP2_MODE1			0x05c
> +#define QSERDES_COM_LOCK_CMP3_MODE1			0x060
> +#define QSERDES_COM_BG_TRIM				0x070
> +#define QSERDES_COM_CLK_EP_DIV				0x074
> +#define QSERDES_COM_CP_CTRL_MODE0			0x078
> +#define QSERDES_COM_CP_CTRL_MODE1			0x07c
> +#define QSERDES_COM_PLL_RCTRL_MODE0			0x084
> +#define QSERDES_COM_PLL_RCTRL_MODE1			0x088
> +#define QSERDES_COM_PLL_CCTRL_MODE0			0x090
> +#define QSERDES_COM_PLL_CCTRL_MODE1			0x094
> +#define QSERDES_COM_SYSCLK_EN_SEL			0x0ac
> +#define QSERDES_COM_RESETSM_CNTRL			0x0b4
> +#define QSERDES_COM_RESTRIM_CTRL			0x0bc
> +#define QSERDES_COM_RESCODE_DIV_NUM			0x0c4
> +#define QSERDES_COM_LOCK_CMP_EN				0x0c8
> +#define QSERDES_COM_LOCK_CMP_CFG			0x0cc
> +#define QSERDES_COM_DEC_START_MODE0			0x0d0
> +#define QSERDES_COM_DEC_START_MODE1			0x0d4
> +#define QSERDES_COM_DIV_FRAC_START1_MODE0		0x0dc
> +#define QSERDES_COM_DIV_FRAC_START2_MODE0		0x0e0
> +#define QSERDES_COM_DIV_FRAC_START3_MODE0		0x0e4
> +#define QSERDES_COM_DIV_FRAC_START1_MODE1		0x0e8
> +#define QSERDES_COM_DIV_FRAC_START2_MODE1		0x0ec
> +#define QSERDES_COM_DIV_FRAC_START3_MODE1		0x0f0
> +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0		0x108
> +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0		0x10c
> +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1		0x110
> +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1		0x114
> +#define QSERDES_COM_VCO_TUNE_CTRL			0x124
> +#define QSERDES_COM_VCO_TUNE_MAP			0x128
> +#define QSERDES_COM_VCO_TUNE1_MODE0			0x12c
> +#define QSERDES_COM_VCO_TUNE2_MODE0			0x130
> +#define QSERDES_COM_VCO_TUNE1_MODE1			0x134
> +#define QSERDES_COM_VCO_TUNE2_MODE1			0x138
> +#define QSERDES_COM_VCO_TUNE_TIMER1			0x144
> +#define QSERDES_COM_VCO_TUNE_TIMER2			0x148
> +#define QSERDES_COM_BG_CTRL				0x170
> +#define QSERDES_COM_CLK_SELECT				0x174
> +#define QSERDES_COM_HSCLK_SEL				0x178
> +#define QSERDES_COM_CORECLK_DIV				0x184
> +#define QSERDES_COM_CORE_CLK_EN				0x18c
> +#define QSERDES_COM_C_READY_STATUS			0x190
> +#define QSERDES_COM_CMN_CONFIG				0x194
> +#define QSERDES_COM_SVS_MODE_CLK_SEL			0x19c
> +#define QSERDES_COM_DEBUG_BUS0				0x1a0
> +#define QSERDES_COM_DEBUG_BUS1				0x1a4
> +#define QSERDES_COM_DEBUG_BUS2				0x1a8
> +#define QSERDES_COM_DEBUG_BUS3				0x1ac
> +#define QSERDES_COM_DEBUG_BUS_SEL			0x1b0
> +#define QSERDES_COM_CORECLK_DIV_MODE1			0x1bc
> +
> +/* QMP PHY TX registers */
> +#define QSERDES_TX_RES_CODE_LANE_OFFSET			0x054
> +#define QSERDES_TX_DEBUG_BUS_SEL			0x064
> +#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN	0x068
> +#define QSERDES_TX_LANE_MODE				0x094
> +#define QSERDES_TX_RCV_DETECT_LVL_2			0x0ac
> +
> +/* QMP PHY RX registers */
> +#define QSERDES_RX_UCDR_SO_GAIN_HALF			0x010
> +#define QSERDES_RX_UCDR_SO_GAIN				0x01c
> +#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN		0x040
> +#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE	0x048
> +#define QSERDES_RX_RX_TERM_BW				0x090
> +#define QSERDES_RX_RX_EQ_GAIN1_LSB			0x0c4
> +#define QSERDES_RX_RX_EQ_GAIN1_MSB			0x0c8
> +#define QSERDES_RX_RX_EQ_GAIN2_LSB			0x0cc
> +#define QSERDES_RX_RX_EQ_GAIN2_MSB			0x0d0
> +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2		0x0d8
> +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3		0x0dc
> +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4		0x0e0
> +#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1		0x108
> +#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2		0x10c
> +#define QSERDES_RX_SIGDET_ENABLES			0x110
> +#define QSERDES_RX_SIGDET_CNTRL				0x114
> +#define QSERDES_RX_SIGDET_LVL				0x118
> +#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL		0x11c
> +#define QSERDES_RX_RX_BAND				0x120
> +#define QSERDES_RX_RX_INTERFACE_MODE			0x12c
> +
> +/* QMP PHY PCS registers */
> +#define QPHY_SW_RESET					0x00
> +#define QPHY_POWER_DOWN_CONTROL				0x04
> +#define QPHY_START_CTRL					0x08
> +#define QPHY_TXDEEMPH_M6DB_V0				0x24
> +#define QPHY_TXDEEMPH_M3P5DB_V0				0x28
> +#define QPHY_ENDPOINT_REFCLK_DRIVE			0x54
> +#define QPHY_RX_IDLE_DTCT_CNTRL				0x58
> +#define QPHY_POWER_STATE_CONFIG1			0x60
> +#define QPHY_POWER_STATE_CONFIG2			0x64
> +#define QPHY_POWER_STATE_CONFIG4			0x6c
> +#define QPHY_LOCK_DETECT_CONFIG1			0x80
> +#define QPHY_LOCK_DETECT_CONFIG2			0x84
> +#define QPHY_LOCK_DETECT_CONFIG3			0x88
> +#define QPHY_PWRUP_RESET_DLY_TIME_AUXCLK		0xa0
> +#define QPHY_LP_WAKEUP_DLY_TIME_AUXCLK			0xa4
> +
> +/* PHY_SW_RESET bit */
> +#define PHY_SW_RESET				BIT(0)
> +/* PHY_POWER_DOWN_CONTROL */
> +#define PHY_SW_PWRDN				BIT(0)
> +#define PHY_REFCLK_DRV_DSBL			BIT(1)
> +/* PHY_START_CONTROL bits */
> +#define PHY_SERDES_START			BIT(0)
> +#define PHY_PCS_START				BIT(1)
> +#define PHY_PLL_READY_GATE_EN			BIT(3)
> +/* PHY_PCS_STATUS bit */
> +#define MASK_PHYSTATUS				BIT(6)
> +/* PCS_READY_STATUS bit */
> +#define MASK_COM_PCS_READY			BIT(0)
> +
> +#define REFCLK_STABILIZATION_DELAY_US_MIN	1000
> +#define REFCLK_STABILIZATION_DELAY_US_MAX	1005
> +#define PHY_READY_TIMEOUT_COUNT			10
> +#define POWER_DOWN_DELAY_US_MIN			10
> +#define POWER_DOWN_DELAY_US_MAX			11
> +
> +#define MAX_PROP_NAME		32
> +
> +struct qmp_phy_init_tbl {
> +	unsigned int reg_offset;
> +	unsigned int cfg_val;
> +	/*
> +	 * register part of layout ?
> +	 * if yes, then reg_offset gives index in the reg-layout
> +	 */
> +	int in_layout;
> +};
> +#define QCOM_QMP_PHY_INIT_CFG(reg, val) \
> +	{				\
> +		.reg_offset = reg,	\
> +		.cfg_val = val,		\
> +	}
> +#define QCOM_QMP_PHY_INIT_CFG_L(reg, val) \
> +	{				  \
> +		.reg_offset = reg,	  \
> +		.cfg_val = val,		  \
> +		.in_layout = 1,		  \
> +	}
> +
> +/* set of registers with offsets different per-PHY */
> +enum qphy_reg_layout {
> +	/* Common block control registers */
> +	QPHY_COM_SW_RESET,
> +	QPHY_COM_POWER_DOWN_CONTROL,
> +	QPHY_COM_START_CONTROL,
> +	QPHY_COM_PCS_READY_STATUS,
> +	/* PCS registers */
> +	QPHY_PLL_LOCK_CHK_DLY_TIME,
> +	QPHY_FLL_CNTRL1,
> +	QPHY_FLL_CNTRL2,
> +	QPHY_FLL_CNT_VAL_L,
> +	QPHY_FLL_CNT_VAL_H_TOL,
> +	QPHY_FLL_MAN_CODE,
> +	QPHY_PCS_READY_STATUS,
> +};
> +
> +unsigned int pciephy_regs_layout[] = {
> +	[QPHY_COM_SW_RESET]		= 0x400,
> +	[QPHY_COM_POWER_DOWN_CONTROL]	= 0x404,
> +	[QPHY_COM_START_CONTROL]	= 0x408,
> +	[QPHY_COM_PCS_READY_STATUS]	= 0x448,
> +	[QPHY_PLL_LOCK_CHK_DLY_TIME]	= 0xa8,
> +	[QPHY_FLL_CNTRL1]		= 0xc4,
> +	[QPHY_FLL_CNTRL2]		= 0xc8,
> +	[QPHY_FLL_CNT_VAL_L]		= 0xcc,
> +	[QPHY_FLL_CNT_VAL_H_TOL]	= 0xd0,
> +	[QPHY_FLL_MAN_CODE]		= 0xd4,
> +	[QPHY_PCS_READY_STATUS]		= 0x174,
> +};
> +
> +unsigned int usb3phy_regs_layout[] = {
> +	[QPHY_FLL_CNTRL1]		= 0xc0,
> +	[QPHY_FLL_CNTRL2]		= 0xc4,
> +	[QPHY_FLL_CNT_VAL_L]		= 0xc8,
> +	[QPHY_FLL_CNT_VAL_H_TOL]	= 0xcc,
> +	[QPHY_FLL_MAN_CODE]		= 0xd0,
> +	[QPHY_PCS_READY_STATUS]		= 0x17c,
> +};
> +
> +static struct qmp_phy_init_tbl pciephy_serdes_init_tbl[] = {
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1c),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x42),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x1f),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x01),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x09),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x1a),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x0a),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x02),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1f),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x04),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x02),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x2f),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x19),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x15),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_EP_DIV, 0x19),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x40),
> +};
> +
> +static struct qmp_phy_init_tbl pciephy_tx_init_tbl[] = {
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06),
> +};
> +
> +static struct qmp_phy_init_tbl pciephy_rx_init_tbl[] = {
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x1c),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x01),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xdb),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_BAND, 0x18),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN_HALF, 0x04),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x14),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x19),
> +};
> +
> +static struct qmp_phy_init_tbl pciephy_pcs_init_tbl[] = {
> +	QCOM_QMP_PHY_INIT_CFG(QPHY_RX_IDLE_DTCT_CNTRL, 0x4c),
> +	QCOM_QMP_PHY_INIT_CFG(QPHY_PWRUP_RESET_DLY_TIME_AUXCLK, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QPHY_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01),
> +
> +	QCOM_QMP_PHY_INIT_CFG_L(QPHY_PLL_LOCK_CHK_DLY_TIME, 0x05),
> +
> +	QCOM_QMP_PHY_INIT_CFG(QPHY_ENDPOINT_REFCLK_DRIVE, 0x05),
> +	QCOM_QMP_PHY_INIT_CFG(QPHY_POWER_DOWN_CONTROL, 0x02),
> +	QCOM_QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG4, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG1, 0xa3),
> +	QCOM_QMP_PHY_INIT_CFG(QPHY_TXDEEMPH_M3P5DB_V0, 0x0e),
> +};
> +
> +static struct qmp_phy_init_tbl usb3phy_serdes_init_tbl[] = {
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x04),
> +	/* PLL and Loop filter settings */
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x15),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x34),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a),
> +	/* SSC settings */
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0xde),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x07),
> +};
> +
> +static struct qmp_phy_init_tbl usb3phy_tx_init_tbl[] = {
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_TX_RCV_DETECT_LVL_2, 0x12),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06),
> +};
> +
> +static struct qmp_phy_init_tbl usb3phy_rx_init_tbl[] = {
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xbb),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x03),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x18),
> +	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x16),
> +};
> +
> +static struct qmp_phy_init_tbl usb3phy_pcs_init_tbl[] = {
> +	/* FLL settings */
> +	QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_CNTRL2, 0x03),
> +	QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_CNTRL1, 0x02),
> +	QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_CNT_VAL_L, 0x09),
> +	QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_CNT_VAL_H_TOL, 0x42),
> +	QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_MAN_CODE, 0x85),
> +
> +	/* Lock Det settings */
> +	QCOM_QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG1, 0xd1),
> +	QCOM_QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG2, 0x1f),
> +	QCOM_QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG3, 0x47),
> +	QCOM_QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG2, 0x08),
> +};

if you can implement the dt mechanism I mentioned in your other patch, all
these tables can be got rid of and the code to initialize these can also be
removed.
> +
> +/**
> + * struct qmp_phy_init_cfg:- per-PHY init config.
> + */
> +struct qmp_phy_init_cfg {
> +	/*
> +	 * @type: phy-type - PCIE/UFS/USB
> +	 * @nlanes: number of lanes provided by phy
> +	 * @tx_offsets: array of offsets of Tx block from PHY base
> +	 * @rx_offsets: array of offsets of Rx block from PHY base
> +	 * @pcs_offsets: array of offsets of PCS block from PHY base
> +	 */
> +	unsigned int type;
> +	int nlanes;
> +	unsigned int *tx_offsets;
> +	unsigned int *rx_offsets;
> +	unsigned int *pcs_offsets;
> +
> +	/* Initialization sequence for PHY blocks - Serdes, tx, rx, pcs */
> +	struct qmp_phy_init_tbl *phy_init_serdes_tbl;
> +	int phy_init_serdes_tbl_sz;
> +	struct qmp_phy_init_tbl *phy_init_tx_tbl;
> +	int phy_init_tx_tbl_sz;
> +	struct qmp_phy_init_tbl *phy_init_rx_tbl;
> +	int phy_init_rx_tbl_sz;
> +	struct qmp_phy_init_tbl *phy_init_pcs_tbl;
> +	int phy_init_pcs_tbl_sz;
> +
> +	/* array of registers with different offsets */
> +	unsigned int *regs;
> +
> +	unsigned int mask_start_ctrl;
> +	unsigned int mask_pwr_dn_ctrl;
> +	/* true, if PHY has a separate PHY_COM_CNTRL block */
> +	bool has_phy_com_ctrl;
> +};
> +
> +/**
> + * struct qmp_phy_desc:- per-lane phy-descriptor.
> + *
> + * @phy: pointer to generic phy
> + * @tx: pointer to iomapped memory space for PHY's tx
> + * @rx: pointer to iomapped memory space for PHY's rx
> + * @pcs: pointer to iomapped memory space for PHY's pcs
> + * @pipe_clk: pointer to pipe lock
> + * @index: lane index
> + * @qphy: pointer to QMP phy to which this lane belongs
> + * @lane_rst: pointer to lane's reset controller
> + */
> +struct qmp_phy_desc {
> +	struct phy *phy;
> +	void __iomem *tx;
> +	void __iomem *rx;
> +	void __iomem *pcs;
> +	struct clk *pipe_clk;
> +	unsigned int index;
> +	struct qcom_qmp_phy *qphy;
> +	struct reset_control *lane_rst;
> +};
> +
> +/**
> + * struct qcom_qmp_phy:- structure holding QMP PHY attributes.
> + *
> + * @dev: pointer to device
> + * @serdes: pointer to iomapped memory space for phy's serdes
> + *
> + * @aux_clk: pointer to phy core clock
> + * @cfg_ahb_clk: pointer to AHB2PHY interface clock
> + * @ref_clk: pointer to reference clock
> + * @ref_clk_src: pointer to source to reference clock
> + * @clk_enabled: check if clocks are enabled or not
> + *
> + * @vdda_phy: vdd supply to the phy core block
> + * @vdda_pll: 1.8V vdd supply to ref_clk block
> + * @vddp_ref_clk: vdd supply to specific ref_clk block
> + *
> + * @phy_rst: Pointer to phy reset control
> + * @phycom_rst: Pointer to phy common reset control
> + * @phycfg_rst: Pointer to phy ahb cfg reset control (Optional)
> + *
> + * @cfg: pointer to init config for each phys
> + * @phys: array of pointer to per-lane phy descriptors
> + * @phy_mutex: mutex lock for PHY common block initialization
> + * @init_count: Phy common block initialization count
> + */
> +struct qcom_qmp_phy {
> +	struct device *dev;
> +	void __iomem *serdes;
> +
> +	struct clk *aux_clk;
> +	struct clk *cfg_ahb_clk;
> +	struct clk *ref_clk;
> +	struct clk *ref_clk_src;
> +	bool clk_enabled;
> +
> +	struct regulator *vdda_phy;
> +	struct regulator *vdda_pll;
> +	struct regulator *vddp_ref_clk;
> +
> +	struct reset_control *phy_rst;
> +	struct reset_control *phycom_rst;
> +	struct reset_control *phycfg_rst;
> +
> +	const struct qmp_phy_init_cfg *cfg;
> +	struct qmp_phy_desc **phys;
> +
> +	struct mutex phy_mutex;
> +	int init_count;
> +};
> +
> +static inline void qphy_setbits(u32 bitmask, void __iomem *reg)
> +{
> +	u32 val;
> +
> +	val = readl_relaxed(reg);
> +	val |= bitmask;
> +	writel_relaxed(val, reg);
> +}
> +
> +static inline void qphy_clrbits(u32 bitmask, void __iomem *reg)
> +{
> +	u32 val;
> +
> +	val = readl_relaxed(reg);
> +	val &= ~bitmask;
> +	writel_relaxed(val, reg);
> +}
> +
> +unsigned int msm8996_pciephy_tx_offsets[] = { 0x1000, 0x2000, 0x3000 };

you can have a separate reg map for each lane and all these can come from dt.
> +unsigned int msm8996_pciephy_rx_offsets[] = { 0x1200, 0x2200, 0x3200 };
> +unsigned int msm8996_pciephy_pcs_offsets[] = { 0x1400, 0x2400, 0x3400 };
> +
> +unsigned int msm8996_usb3phy_tx_offsets[] = { 0x200 };
> +unsigned int msm8996_usb3phy_rx_offsets[] = { 0x400 };
> +unsigned int msm8996_usb3phy_pcs_offsets[] = { 0x600 };
> +
> +const struct qmp_phy_init_cfg pciephy_init_cfg = {
> +	.type			= PHY_TYPE_PCIE,
> +	.nlanes			= 3,
> +	.tx_offsets		= msm8996_pciephy_tx_offsets,
> +	.rx_offsets		= msm8996_pciephy_rx_offsets,
> +	.pcs_offsets		= msm8996_pciephy_pcs_offsets,
> +
> +	.phy_init_serdes_tbl	= pciephy_serdes_init_tbl,
> +	.phy_init_serdes_tbl_sz	= ARRAY_SIZE(pciephy_serdes_init_tbl),
> +	.phy_init_tx_tbl	= pciephy_tx_init_tbl,
> +	.phy_init_tx_tbl_sz	= ARRAY_SIZE(pciephy_tx_init_tbl),
> +	.phy_init_rx_tbl	= pciephy_rx_init_tbl,
> +	.phy_init_rx_tbl_sz	= ARRAY_SIZE(pciephy_rx_init_tbl),
> +	.phy_init_pcs_tbl	= pciephy_pcs_init_tbl,
> +	.phy_init_pcs_tbl_sz	= ARRAY_SIZE(pciephy_pcs_init_tbl),
> +	.has_phy_com_ctrl	= true,
> +	.regs			= pciephy_regs_layout,
> +	.mask_start_ctrl	= (PHY_PCS_START | PHY_PLL_READY_GATE_EN),
> +	.mask_pwr_dn_ctrl	= (PHY_SW_PWRDN | PHY_REFCLK_DRV_DSBL),
> +};
> +
> +const struct qmp_phy_init_cfg usb3phy_init_cfg = {
> +	.type			= PHY_TYPE_USB3,
> +	.nlanes			= 1,
> +	.tx_offsets		= msm8996_usb3phy_tx_offsets,
> +	.rx_offsets		= msm8996_usb3phy_rx_offsets,
> +	.pcs_offsets		= msm8996_usb3phy_pcs_offsets,
> +
> +	.phy_init_serdes_tbl	= usb3phy_serdes_init_tbl,
> +	.phy_init_serdes_tbl_sz	= ARRAY_SIZE(usb3phy_serdes_init_tbl),
> +	.phy_init_tx_tbl	= usb3phy_tx_init_tbl,
> +	.phy_init_tx_tbl_sz	= ARRAY_SIZE(usb3phy_tx_init_tbl),
> +	.phy_init_rx_tbl	= usb3phy_rx_init_tbl,
> +	.phy_init_rx_tbl_sz	= ARRAY_SIZE(usb3phy_rx_init_tbl),
> +	.phy_init_pcs_tbl	= usb3phy_pcs_init_tbl,
> +	.phy_init_pcs_tbl_sz	= ARRAY_SIZE(usb3phy_pcs_init_tbl),
> +	.regs			= usb3phy_regs_layout,
> +	.mask_start_ctrl	= (PHY_SERDES_START | PHY_PCS_START),
> +	.mask_pwr_dn_ctrl	= PHY_SW_PWRDN,
> +};
> +
> +static void qcom_qmp_phy_configure(void __iomem *base,
> +				unsigned int *regs_layout,
> +				struct qmp_phy_init_tbl init_tbl[],
> +				int init_tbl_sz)
> +{
> +	int i;
> +
> +	for (i = 0; i < init_tbl_sz; i++) {
> +		if (init_tbl[i].in_layout)
> +			writel_relaxed(init_tbl[i].cfg_val,
> +				base + regs_layout[init_tbl[i].reg_offset]);
> +		else
> +			writel_relaxed(init_tbl[i].cfg_val,
> +				base + init_tbl[i].reg_offset);
> +	}
> +
> +	/* flush buffered writes */
> +	mb();
> +}
> +
> +static int qcom_qmp_phy_poweron(struct phy *phy)
> +{
> +	struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
> +	struct qcom_qmp_phy *qphy = phydesc->qphy;
> +	int ret;
> +
> +	dev_info(&phy->dev, "Powering on QMP phy\n");
> +
> +	ret = regulator_enable(qphy->vdda_phy);
> +	if (ret) {
> +		dev_err(qphy->dev, "%s: vdda-phy enable failed, err=%d\n",
> +				__func__, ret);
> +		return ret;
> +	}
> +
> +	ret = regulator_enable(qphy->vdda_pll);
> +	if (ret) {
> +		dev_err(qphy->dev, "%s: vdda-pll enable failed, err=%d\n",
> +				__func__, ret);
> +		goto err_vdda_pll;
> +	}
> +
> +	if (qphy->vddp_ref_clk) {
> +		ret = regulator_enable(qphy->vddp_ref_clk);
> +		if (ret) {
> +			dev_err(qphy->dev, "%s: vdda-ref-clk enable failed, err=%d\n",
> +					__func__, ret);
> +			goto err_vddp_refclk;
> +		}
> +	}
> +
> +	if (!qphy->clk_enabled) {

lot of my comments on the previous PHY driver is applicable here. For example
the clk_enabled is not required.
> +		clk_prepare_enable(qphy->ref_clk_src);
> +		clk_prepare_enable(qphy->ref_clk);
> +		clk_prepare_enable(phydesc->pipe_clk);
> +		qphy->clk_enabled = true;
> +	}
> +
> +	return 0;
> +
> +err_vddp_refclk:
> +	regulator_disable(qphy->vdda_pll);
> +err_vdda_pll:
> +	regulator_disable(qphy->vdda_phy);
> +	return ret;
> +}
> +
> +static int qcom_qmp_phy_poweroff(struct phy *phy)
> +{
> +	struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
> +	struct qcom_qmp_phy *qphy = phydesc->qphy;
> +
> +	if (qphy->clk_enabled) {
> +		clk_disable_unprepare(qphy->ref_clk_src);
> +		clk_disable_unprepare(qphy->ref_clk);
> +		clk_disable_unprepare(phydesc->pipe_clk);
> +		qphy->clk_enabled = false;
> +	}
> +
> +	if (qphy->vddp_ref_clk)
> +		regulator_disable(qphy->vddp_ref_clk);
> +
> +	regulator_disable(qphy->vdda_pll);
> +	regulator_disable(qphy->vdda_phy);
> +
> +	return 0;
> +}
> +
> +static int qcom_qmp_phy_is_ready(struct qcom_qmp_phy *qphy,
> +				void __iomem *pcs_status, u32 mask)
> +{
> +	unsigned int init_timeout;
> +
> +	init_timeout = PHY_READY_TIMEOUT_COUNT;
> +	do {
> +		if (readl_relaxed(pcs_status) & mask)
> +			break;
> +
> +		usleep_range(REFCLK_STABILIZATION_DELAY_US_MIN,
> +				 REFCLK_STABILIZATION_DELAY_US_MAX);
> +	} while (--init_timeout);
> +
> +	if (!init_timeout)
> +		return -EBUSY;
> +
> +	return 0;
> +}
> +
> +static int qcom_qmp_phy_com_init(struct qcom_qmp_phy *qphy)
> +{
> +	const struct qmp_phy_init_cfg *cfg = qphy->cfg;
> +	void __iomem *serdes = qphy->serdes;
> +	int ret;
> +
> +	mutex_lock(&qphy->phy_mutex);
> +	if (qphy->init_count++) {
> +		mutex_unlock(&qphy->phy_mutex);
> +		return 0;
> +	}
> +
> +	ret = reset_control_deassert(qphy->phy_rst);
> +	if (ret) {
> +		dev_err(qphy->dev, "phy reset deassert failed\n");
> +		return ret;
> +	}
> +
> +	ret = reset_control_deassert(qphy->phycom_rst);
> +	if (ret) {
> +		dev_err(qphy->dev, "common reset deassert failed\n");
> +		goto err_phycom_rst;
> +	}
> +
> +	if (qphy->phycfg_rst) {
> +		ret = reset_control_deassert(qphy->phycfg_rst);
> +		if (ret) {
> +			dev_err(qphy->dev, "common reset deassert failed\n");
> +			goto err_phycfg_rst;
> +		}
> +	}
> +
> +	if (cfg->has_phy_com_ctrl) {
> +		qphy_setbits(PHY_SW_PWRDN,
> +			serdes + cfg->regs[QPHY_COM_POWER_DOWN_CONTROL]);
> +		/* Make sure that above write is completed */
> +		mb();
> +	}
> +
> +	/* Serdes configuration */
> +	qcom_qmp_phy_configure(serdes, cfg->regs, cfg->phy_init_serdes_tbl,
> +				cfg->phy_init_serdes_tbl_sz);
> +
> +	if (cfg->has_phy_com_ctrl) {
> +		qphy_clrbits(PHY_SW_RESET, serdes +
> +					cfg->regs[QPHY_COM_SW_RESET]);
> +		qphy_setbits(PHY_SERDES_START | PHY_PCS_START,
> +				serdes + cfg->regs[QPHY_COM_START_CONTROL]);
> +		/* Make sure that above write is completed */
> +		mb();
> +
> +		ret = qcom_qmp_phy_is_ready(qphy, serdes +
> +					cfg->regs[QPHY_COM_PCS_READY_STATUS],
> +					MASK_COM_PCS_READY);
> +		if (ret) {
> +			dev_err(qphy->dev,
> +				"common control block init timed-out\n");
> +			goto err_phy_comctrl;
> +		}
> +	}
> +
> +	mutex_unlock(&qphy->phy_mutex);
> +
> +	return 0;
> +
> +err_phy_comctrl:
> +	if (qphy->phycfg_rst)
> +		reset_control_assert(qphy->phycfg_rst);
> +err_phycfg_rst:
> +	reset_control_assert(qphy->phycom_rst);
> +err_phycom_rst:
> +	reset_control_assert(qphy->phy_rst);
> +	return ret;
> +}
> +
> +static int qcom_qmp_phy_com_exit(struct qcom_qmp_phy *qphy)
> +{
> +	const struct qmp_phy_init_cfg *cfg = qphy->cfg;
> +	void __iomem *serdes = qphy->serdes;
> +
> +	mutex_lock(&qphy->phy_mutex);
> +	if (--qphy->init_count) {
> +		mutex_unlock(&qphy->phy_mutex);
> +		return 0;
> +	}
> +
> +	if (cfg->has_phy_com_ctrl) {
> +		qphy_setbits(PHY_SERDES_START | PHY_PCS_START,
> +				serdes + cfg->regs[QPHY_COM_START_CONTROL]);
> +		qphy_clrbits(PHY_SW_RESET, serdes +
> +					cfg->regs[QPHY_COM_SW_RESET]);
> +		qphy_setbits(PHY_SW_PWRDN,
> +			serdes + cfg->regs[QPHY_COM_POWER_DOWN_CONTROL]);
> +
> +		/* Make sure that above writes are completed */
> +		mb();
> +	}
> +
> +	reset_control_assert(qphy->phy_rst);
> +	reset_control_assert(qphy->phycom_rst);
> +	if (qphy->phycfg_rst)
> +		reset_control_assert(qphy->phycfg_rst);
> +
> +	mutex_unlock(&qphy->phy_mutex);
> +
> +	return 0;
> +}
> +
> +/* PHY Initialization */
> +static int qcom_qmp_phy_init(struct phy *phy)
> +{
> +	struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
> +	struct qcom_qmp_phy *qphy = phydesc->qphy;
> +	const struct qmp_phy_init_cfg *cfg = qphy->cfg;
> +	void __iomem *tx = phydesc->tx;
> +	void __iomem *rx = phydesc->rx;
> +	void __iomem *pcs = phydesc->pcs;
> +	int ret;
> +
> +	dev_info(qphy->dev, "Initializing QMP phy\n");
> +
> +	/* enable interface clocks to program phy */
> +	clk_prepare_enable(qphy->aux_clk);
> +	clk_prepare_enable(qphy->cfg_ahb_clk);
> +
> +	ret = qcom_qmp_phy_com_init(qphy);
> +	if (ret)
> +		goto err;
> +
> +	if (phydesc->lane_rst) {
> +		ret = reset_control_deassert(phydesc->lane_rst);
> +		if (ret) {
> +			dev_err(qphy->dev, "lane<%d> reset deassert failed\n",
> +					phydesc->index);
> +			goto err_lane_rst;
> +		}
> +	}
> +
> +	/* Tx, Rx, and PCS configurations */
> +	qcom_qmp_phy_configure(tx, cfg->regs, cfg->phy_init_tx_tbl,
> +				cfg->phy_init_tx_tbl_sz);
> +	qcom_qmp_phy_configure(rx, cfg->regs, cfg->phy_init_rx_tbl,
> +				cfg->phy_init_rx_tbl_sz);
> +	qcom_qmp_phy_configure(pcs, cfg->regs, cfg->phy_init_pcs_tbl,
> +				cfg->phy_init_pcs_tbl_sz);
> +
> +	/*
> +	 * Pull out PHY from POWER DOWN state:
> +	 * This is active low enable signal to power-down PHY.
> +	 */
> +	qphy_setbits(cfg->mask_pwr_dn_ctrl,
> +			pcs + QPHY_POWER_DOWN_CONTROL);
> +	/* XXX: 10 us delay; given in PCIE HPG only */
> +	usleep_range(POWER_DOWN_DELAY_US_MIN, POWER_DOWN_DELAY_US_MAX);
> +
> +	/* start SerDes and Phy-Coding-Sublayer */
> +	qphy_setbits(cfg->mask_start_ctrl, pcs + QPHY_START_CTRL);
> +
> +	/* Pull PHY out of reset state */
> +	qphy_clrbits(PHY_SW_RESET, pcs + QPHY_SW_RESET);
> +	/* Make sure that above writes are completed */
> +	mb();
> +
> +	ret = qcom_qmp_phy_is_ready(qphy, pcs +
> +					cfg->regs[QPHY_PCS_READY_STATUS],
> +					MASK_PHYSTATUS);
> +	if (ret) {
> +		dev_err(qphy->dev, "phy initialization timed-out\n");
> +		goto err_pcs_ready;
> +	}
> +
> +	return 0;
> +
> +err_pcs_ready:
> +	if (phydesc->lane_rst)
> +		reset_control_assert(phydesc->lane_rst);
> +err_lane_rst:
> +	qcom_qmp_phy_com_exit(qphy);
> +err:
> +	clk_disable_unprepare(qphy->cfg_ahb_clk);
> +	clk_disable_unprepare(qphy->aux_clk);
> +	return ret;
> +}
> +
> +static int qcom_qmp_phy_exit(struct phy *phy)
> +{
> +	struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
> +	struct qcom_qmp_phy *qphy = phydesc->qphy;
> +	const struct qmp_phy_init_cfg *cfg = qphy->cfg;
> +
> +	/* PHY reset */
> +	qphy_setbits(PHY_SW_RESET, phydesc->pcs + QPHY_SW_RESET);
> +
> +	/* stop SerDes and Phy-Coding-Sublayer */
> +	qphy_clrbits(cfg->mask_start_ctrl, phydesc->pcs + QPHY_START_CTRL);
> +
> +	/* Put PHY into POWER DOWN state: active low */
> +	qphy_clrbits(cfg->mask_pwr_dn_ctrl,
> +			phydesc->pcs + QPHY_POWER_DOWN_CONTROL);
> +
> +	/* Make sure that above writes are completed */
> +	mb();
> +
> +	if (phydesc->lane_rst)
> +		reset_control_assert(phydesc->lane_rst);
> +
> +	qcom_qmp_phy_com_exit(qphy);
> +
> +	clk_disable_unprepare(qphy->aux_clk);
> +	clk_disable_unprepare(qphy->cfg_ahb_clk);
> +
> +	return 0;
> +}
> +
> +
> +static int qcom_qmp_phy_regulator_init(struct device *dev)
> +{
> +	struct qcom_qmp_phy *qphy = dev_get_drvdata(dev);
> +	int ret = 0;
> +
> +	qphy->vdda_phy = devm_regulator_get(dev, "vdda-phy");
> +	if (IS_ERR(qphy->vdda_phy)) {
> +		ret = PTR_ERR(qphy->vdda_phy);
> +		dev_err(dev, "failed to get vdda-phy, %d\n", ret);
> +		return ret;
> +	}
> +
> +	qphy->vdda_pll = devm_regulator_get(dev, "vdda-pll");
> +	if (IS_ERR(qphy->vdda_pll)) {
> +		ret = PTR_ERR(qphy->vdda_pll);
> +		dev_err(dev, "failed to get vdda-pll, %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* optional regulator */
> +	qphy->vddp_ref_clk = devm_regulator_get(dev, "vddp-ref-clk");
> +	if (IS_ERR(qphy->vddp_ref_clk)) {
> +		ret = PTR_ERR(qphy->vddp_ref_clk);
> +		dev_info(dev, "failed to get vddp-ref-clk, %d\n", ret);
> +		qphy->vddp_ref_clk = NULL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int qcom_qmp_phy_clk_init(struct device *dev)
> +{
> +	struct qcom_qmp_phy *qphy = dev_get_drvdata(dev);
> +	int ret;
> +
> +	qphy->aux_clk = devm_clk_get(dev, "aux");
> +	if (IS_ERR(qphy->aux_clk)) {
> +		ret = PTR_ERR(qphy->aux_clk);
> +		if (ret != -EPROBE_DEFER)
> +			dev_err(dev, "failed to get aux_clk\n");
> +		return ret;
> +	}
> +
> +	qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb");
> +	if (IS_ERR(qphy->cfg_ahb_clk)) {
> +		ret = PTR_ERR(qphy->cfg_ahb_clk);
> +		if (ret != -EPROBE_DEFER)
> +			dev_err(dev, "failed to get cfg_ahb_clk\n");
> +		return ret;
> +	}
> +
> +	/*
> +	 * ref_clk and ref_clk_src handles may not be available in
> +	 * all hardwares. So we don't return error in these cases.
> +	 */
> +	qphy->ref_clk_src = devm_clk_get(dev, "ref_clk_src");
> +	if (IS_ERR(qphy->ref_clk_src)) {
> +		ret = PTR_ERR(qphy->ref_clk_src);
> +		if (ret != -EPROBE_DEFER) {
> +			qphy->ref_clk_src = NULL;
> +			dev_dbg(dev, "failed to get ref_clk_src\n");
> +		} else {
> +			return ret;
> +		}
> +	}
> +
> +	qphy->ref_clk = devm_clk_get(dev, "ref_clk");
> +	if (IS_ERR(qphy->ref_clk)) {
> +		ret = PTR_ERR(qphy->ref_clk);
> +		if (ret != -EPROBE_DEFER) {
> +			qphy->ref_clk = NULL;
> +			dev_dbg(dev, "failed to get ref_clk\n");
> +		} else {
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static struct phy *qcom_qmp_phy_xlate(struct device *dev,
> +					struct of_phandle_args *args)
> +{
> +	struct qcom_qmp_phy *qphy = dev_get_drvdata(dev);
> +	int i;
> +
> +	if (WARN_ON(args->args[0] >= qphy->cfg->nlanes))
> +		return ERR_PTR(-ENODEV);
> +
> +	for (i = 0; i < qphy->cfg->nlanes; i++) {
> +		if (qphy->phys[i]->index == args->args[0])
> +			break;

finding a PHY based on index is not required. Just have a different label for
each sub-node and using this label in the controller node should be enough.

Thanks
Kishon

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] phy: qcom-qusb2: New driver for QUSB2 PHY on Qcom chips
  2016-10-19 10:43 ` [PATCH 1/2] phy: qcom-qusb2: New driver for QUSB2 PHY on Qcom chips Vivek Gautam
  2016-10-20  4:13   ` Vivek Gautam
  2016-10-26 19:56   ` Kishon Vijay Abraham I
@ 2016-10-26 21:16   ` Rob Herring
  2016-11-02  8:11     ` Vivek Gautam
  2 siblings, 1 reply; 19+ messages in thread
From: Rob Herring @ 2016-10-26 21:16 UTC (permalink / raw)
  To: Vivek Gautam
  Cc: kishon, mark.rutland, devicetree, linux-kernel, linux-arm-msm

On Wed, Oct 19, 2016 at 04:13:46PM +0530, Vivek Gautam wrote:
> PHY transceiver driver for QUSB2 phy controller that provides
> HighSpeed functionality for DWC3 controller present on
> Qualcomm chipsets.
> 
> This driver is based on phy-msm-qusb driver available in
> msm-4.4 kernel @codeaurora[1]
> 
> [1] https://source.codeaurora.org/quic/la/kernel/msm-4.4/log/?h=caf/3.18/msm-3.18
> 
> Signed-off-by: Vivek Gautam <vivek.gautam@codeaurora.org>
> Cc: Kishon Vijay Abraham I <kishon@ti.com>
> ---
>  .../devicetree/bindings/phy/qcom-qusb2-phy.txt     |  37 ++

It's preferred the bindings are a separate patch.

>  drivers/phy/Kconfig                                |  10 +
>  drivers/phy/Makefile                               |   1 +
>  drivers/phy/phy-qcom-qusb2.c                       | 577 +++++++++++++++++++++
>  4 files changed, 625 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt
>  create mode 100644 drivers/phy/phy-qcom-qusb2.c
> 
> diff --git a/Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt b/Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt
> new file mode 100644
> index 0000000..97c9ce7
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt
> @@ -0,0 +1,37 @@
> +Qualcomm QUSB2 phy controller
> +=============================
> +
> +QUSB2 controller supports LS/FS/HS usb connectivity on Qualcomm chipsets.
> +
> +Required properties:
> + - compatible: compatible list, contains "qcom,msm8996-qusb2-phy".
> + - reg: offset and length of the PHY register set.
> + - #phy-cells: must be 0.
> +
> + - clocks: a list of phandles and clock-specifier pairs,
> +	   one for each entry in clock-names.
> + - clock-names: must be "cfg_ahb" for phy config clock,
> +			"ref_clk" for 19.2 MHz ref clk,
> +			"ref_clk_src" reference clock source.
> +			"iface" for phy interface clock (Optional).
> +
> + - vdd-phy-supply: Phandle to a regulator supply to PHY core block.
> + - vdda-pll-supply: Phandle to 1.8V regulator supply to PHY refclk pll block.
> + - vdda-phy-dpdm: Phandle to 3.1V regulator supply to Dp/Dm port signals.
> +
> + - resets: a list of phandles and reset controller specifier pairs,
> +	   one for each entry in reset-names.
> + - reset-names: must be "phy" for reset of phy block.
> +
> +Optional properties:
> + - nvmem-cells: a list of phandles to nvmem cells that contain fused
> +		tuning parameters for qusb2 phy, one for each entry
> +		in nvmem-cell-names.
> + - nvmem-cell-names: must be "tune2_hstx_trim_efuse" for cell containing
> +		     HS Tx trim value.
> + - qcom,hstx-trim-bit-offset: bit offset within nvmem cell for
> +			      HS Tx trim value.
> + - qcom,hstx-trim-bit-len: bit length of HS Tx trim value within nvmem cell.

When does this change? Why is it not just a different nvmem cell?

> +
> + - qcom,tcsr-syscon: Phandle to TCSR syscon register region.
> + - qcom,phy-clk-scheme: Offset to TCSR_PHY_CLK_SCHEME_SEL register.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] phy: qcom-qmp: new qmp phy driver for qcom-chipsets
       [not found]   ` <1476873827-7191-3-git-send-email-vivek.gautam-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
  2016-10-26 13:57     ` Srinivas Kandagatla
@ 2016-10-26 21:18     ` Rob Herring
  2016-11-02  7:27       ` Vivek Gautam
  1 sibling, 1 reply; 19+ messages in thread
From: Rob Herring @ 2016-10-26 21:18 UTC (permalink / raw)
  To: Vivek Gautam
  Cc: kishon-l0cyMroinI0, mark.rutland-5wv7dgnIgG8,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA

On Wed, Oct 19, 2016 at 04:13:47PM +0530, Vivek Gautam wrote:
> Qualcomm SOCs have QMP phy controller that provides support
> to a number of controller, viz. PCIe, UFS, and USB.
> Add a new driver, based on generic phy framework, for this
> phy controller.
> 
> USB3-phy changes: Based on phy-msm-ssusb-qmp driver available on
> msm-4.4 kernel @codeaurora[1].
> PCIe-phy changes: Based on msm8996-pcie-phy driver posted by
> Srinivas [2].
> 
> [1] https://source.codeaurora.org/quic/la/kernel/msm-4.4/log/?h=caf/3.18/msm-3.18
> [2] https://patchwork.kernel.org/patch/9318947/
> 
> Signed-off-by: Vivek Gautam <vivek.gautam-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> Cc: Kishon Vijay Abraham I <kishon-l0cyMroinI0@public.gmane.org>
> ---
>  .../devicetree/bindings/phy/qcom-qmp-phy.txt       |   61 ++

Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

>  drivers/phy/Kconfig                                |    8 +
>  drivers/phy/Makefile                               |    1 +
>  drivers/phy/phy-qcom-qmp.c                         | 1154 ++++++++++++++++++++
>  4 files changed, 1224 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
>  create mode 100644 drivers/phy/phy-qcom-qmp.c
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] phy: qcom-qmp: new qmp phy driver for qcom-chipsets
       [not found]       ` <a969a6be-472d-a463-790b-1e2f373d19b2-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
@ 2016-11-02  7:26         ` Vivek Gautam
  2016-11-02  9:33           ` Srinivas Kandagatla
  0 siblings, 1 reply; 19+ messages in thread
From: Vivek Gautam @ 2016-11-02  7:26 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: kishon, robh+dt, Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA

Hi Srini,


On Wed, Oct 26, 2016 at 7:27 PM, Srinivas Kandagatla
<srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
> Hi Vivek,
>
> Thanks for consolidating qmp phy support for both usb and pcie.

On vacation, so responses are bit late. Please bear with me.

>
> On 19/10/16 11:43, Vivek Gautam wrote:
>>
>> Qualcomm SOCs have QMP phy controller that provides support
>> to a number of controller, viz. PCIe, UFS, and USB.
>> Add a new driver, based on generic phy framework, for this
>> phy controller.
>>
>> USB3-phy changes: Based on phy-msm-ssusb-qmp driver available on
>> msm-4.4 kernel @codeaurora[1].
>> PCIe-phy changes: Based on msm8996-pcie-phy driver posted by
>> Srinivas [2].
>>
>> [1]
>> https://source.codeaurora.org/quic/la/kernel/msm-4.4/log/?h=caf/3.18/msm-3.18
>> [2] https://patchwork.kernel.org/patch/9318947/
>>
>> Signed-off-by: Vivek Gautam <vivek.gautam-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
>> Cc: Kishon Vijay Abraham I <kishon-l0cyMroinI0@public.gmane.org>
>
>
> very few minor nits which needs fixing in next version.
>
> I have tested this patch on DB820c PCIE with ethernet and SATA ports.
>
> Tested-by: Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

Thanks for testing the patch.

>
>
>> ---
>>  .../devicetree/bindings/phy/qcom-qmp-phy.txt       |   61 ++
>>  drivers/phy/Kconfig                                |    8 +
>>  drivers/phy/Makefile                               |    1 +
>>  drivers/phy/phy-qcom-qmp.c                         | 1154
>> ++++++++++++++++++++
>>  4 files changed, 1224 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
>>  create mode 100644 drivers/phy/phy-qcom-qmp.c
>>
>> diff --git a/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
>> b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
>> new file mode 100644
>> index 0000000..90214aa
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
>> @@ -0,0 +1,61 @@
>> +Qualcomm QMP PHY
>> +----------------
>> +
>> +QMP phy controller supports physical layer functionality for a number of
>> +controllers on Qualcomm chipsets, such as, PCIe, UFS, and USB.
>> +
>> +Required properties:
>> + - compatible: compatible list, contains:
>> +              "qcom,msm8996-qmp-pcie-phy" for 14nm PCIe phy on msm8996,
>> +              "qcom,msm8996-qmp-usb3-phy" for 14nm USB3 phy on msm8996.
>> + - reg: offset and length of the PHY register set.
>> + - #phy-cells: must be 1
>> +    - Cell after phy phandle should be the port (lane) number.
>> + - clocks: a list of phandles and clock-specifier pairs,
>> +          one for each entry in clock-names.
>> + - clock-names: must be "cfg_ahb" for phy config clock,
>> +                       "aux" for phy aux clock,
>> +                       "ref_clk" for 19.2 MHz ref clk,
>> +                       "ref_clk_src" for reference clock source,
>> +                       "pipe<port-number>" for pipe clock specific to
>> +                       each port/lane (Optional).
>
>
>> + - resets: a list of phandles and reset controller specifier pairs,
>> +          one for each entry in reset-names.
>> + - reset-names: must be "phy" for reset of phy block,
>> +                       "common" for phy common block reset,
>> +                       "cfg" for phy's ahb cfg block reset (Optional).
>> +                       "port<port-number>" for reset specific to
>> +                       each port/lane. (Optional)
>> + - vdda-phy-supply: Phandle to a regulator supply to PHY core block.
>> + - vdda-pll-supply: Phandle to 1.8V regulator supply to PHY refclk pll
>> block.
>> +
>> +Optional properties:
>> + - vddp-ref-clk-supply: Phandle to a regulator supply to any specific
>> refclk
>> +                       pll block.
>> +
>> +Example:
>> +       pcie_phy: pciephy@34000 {
>> +               compatible = "qcom,qmp-14nm-pcie-phy";
>
>
> Fix this according to the compatibilities listed above.

Sure, will fix this.

>
>
>> +               reg = <0x034000 0x3fff>;
>> +               #phy-cells = <1>;
>> +
>> +               clocks = <&gcc GCC_PCIE_PHY_AUX_CLK>,
>> +                       <&gcc GCC_PCIE_PHY_CFG_AHB_CLK>,
>> +                       <&gcc GCC_PCIE_0_PIPE_CLK>,
>> +                       <&gcc GCC_PCIE_1_PIPE_CLK>,
>> +                       <&gcc GCC_PCIE_2_PIPE_CLK>;
>> +               clock-names = "aux", "cfg_ahb",
>> +                               "pipe0", "pipe1", "pipe2";
>> +
>> +               vdda-phy-supply = <&pm8994_l28>;
>> +               vdda-pll-supply = <&pm8994_l12>;
>> +
>> +               resets = <&gcc GCC_PCIE_PHY_BCR>,
>> +                       <&gcc GCC_PCIE_PHY_COM_BCR>,
>> +                       <&gcc GCC_PCIE_PHY_COM_NOCSR_BCR>,
>> +                       <&gcc GCC_PCIE_0_PHY_BCR>,
>> +                       <&gcc GCC_PCIE_1_PHY_BCR>,
>> +                       <&gcc GCC_PCIE_2_PHY_BCR>;
>> +               reset-names = "phy", "common", "cfg",
>> +                               "lane0", "lane1", "lane2";
>> +       };
>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>> index 5547984..d5e2b50f 100644
>> --- a/drivers/phy/Kconfig
>> +++ b/drivers/phy/Kconfig
>> @@ -446,6 +446,14 @@ config PHY_STIH41X_USB
>>           Enable this to support the USB transceiver that is part of
>>           STMicroelectronics STiH41x SoC series.
>>
>> +config PHY_QCOM_QMP
>> +       tristate "Qualcomm QMP PHY Driver"
>> +       depends on OF && (ARCH_QCOM || COMPILE_TEST)
>> +       select GENERIC_PHY
>> +       help
>> +         Enable this to support the QMP PHY transceiver that is used
>> +         with controllers such as PCIe, UFS, and USB on Qualcomm chips.
>> +
>>  config PHY_QCOM_QUSB2
>>         tristate "Qualcomm QUSB2 PHY Driver"
>>         depends on OF && (ARCH_QCOM || COMPILE_TEST)
>> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
>> index 848489d..fde9fba 100644
>> --- a/drivers/phy/Makefile
>> +++ b/drivers/phy/Makefile
>> @@ -51,6 +51,7 @@ obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)  +=
>> phy-spear1340-miphy.o
>>  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_QMP)     += phy-qcom-qmp.o
>>  obj-$(CONFIG_PHY_QCOM_QUSB2)   += phy-qcom-qusb2.o
>>  obj-$(CONFIG_PHY_QCOM_UFS)     += phy-qcom-ufs.o
>>  obj-$(CONFIG_PHY_QCOM_UFS)     += phy-qcom-ufs-qmp-20nm.o
>> diff --git a/drivers/phy/phy-qcom-qmp.c b/drivers/phy/phy-qcom-qmp.c
>> new file mode 100644
>> index 0000000..7e89179
>> --- /dev/null
>> +++ b/drivers/phy/phy-qcom-qmp.c
>> @@ -0,0 +1,1154 @@
>> +/*
>> + * Copyright (c) 2016, The Linux Foundation. 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/delay.h>
>> +#include <linux/err.h>
>> +#include <linux/io.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/phy/phy.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/reset.h>
>> +#include <linux/slab.h>
>> +
>> +#include <dt-bindings/phy/phy.h>
>
> We would not need this once you have proper flags in cfg.

We can probably use the PHY_TYPE_{*} macros for pipe_clk.
Please see my comments in the related section below.

>
>
>> +
>> +
>> +static inline void qphy_setbits(u32 bitmask, void __iomem *reg)
>> +{
>> +       u32 val;
>> +
>> +       val = readl_relaxed(reg);
>> +       val |= bitmask;
>> +       writel_relaxed(val, reg);
>> +}
>> +
>> +static inline void qphy_clrbits(u32 bitmask, void __iomem *reg)
>> +{
>> +       u32 val;
>> +
>> +       val = readl_relaxed(reg);
>> +       val &= ~bitmask;
>> +       writel_relaxed(val, reg);
>> +}
>> +
>> +unsigned int msm8996_pciephy_tx_offsets[] = { 0x1000, 0x2000, 0x3000 };
>> +unsigned int msm8996_pciephy_rx_offsets[] = { 0x1200, 0x2200, 0x3200 };
>> +unsigned int msm8996_pciephy_pcs_offsets[] = { 0x1400, 0x2400, 0x3400 };
>> +
>> +unsigned int msm8996_usb3phy_tx_offsets[] = { 0x200 };
>> +unsigned int msm8996_usb3phy_rx_offsets[] = { 0x400 };
>> +unsigned int msm8996_usb3phy_pcs_offsets[] = { 0x600 };
>> +
>> +const struct qmp_phy_init_cfg pciephy_init_cfg = {
>> +       .type                   = PHY_TYPE_PCIE,
>> +       .nlanes                 = 3,
>> +       .tx_offsets             = msm8996_pciephy_tx_offsets,
>> +       .rx_offsets             = msm8996_pciephy_rx_offsets,
>> +       .pcs_offsets            = msm8996_pciephy_pcs_offsets,
>> +
>> +       .phy_init_serdes_tbl    = pciephy_serdes_init_tbl,
>> +       .phy_init_serdes_tbl_sz = ARRAY_SIZE(pciephy_serdes_init_tbl),
>> +       .phy_init_tx_tbl        = pciephy_tx_init_tbl,
>> +       .phy_init_tx_tbl_sz     = ARRAY_SIZE(pciephy_tx_init_tbl),
>> +       .phy_init_rx_tbl        = pciephy_rx_init_tbl,
>> +       .phy_init_rx_tbl_sz     = ARRAY_SIZE(pciephy_rx_init_tbl),
>> +       .phy_init_pcs_tbl       = pciephy_pcs_init_tbl,
>> +       .phy_init_pcs_tbl_sz    = ARRAY_SIZE(pciephy_pcs_init_tbl),
>> +       .has_phy_com_ctrl       = true,
>> +       .regs                   = pciephy_regs_layout,
>> +       .mask_start_ctrl        = (PHY_PCS_START | PHY_PLL_READY_GATE_EN),
>> +       .mask_pwr_dn_ctrl       = (PHY_SW_PWRDN | PHY_REFCLK_DRV_DSBL),
>> +};
>> +
>> +const struct qmp_phy_init_cfg usb3phy_init_cfg = {
>> +       .type                   = PHY_TYPE_USB3,
>> +       .nlanes                 = 1,
>> +       .tx_offsets             = msm8996_usb3phy_tx_offsets,
>> +       .rx_offsets             = msm8996_usb3phy_rx_offsets,
>> +       .pcs_offsets            = msm8996_usb3phy_pcs_offsets,
>> +
>> +       .phy_init_serdes_tbl    = usb3phy_serdes_init_tbl,
>> +       .phy_init_serdes_tbl_sz = ARRAY_SIZE(usb3phy_serdes_init_tbl),
>> +       .phy_init_tx_tbl        = usb3phy_tx_init_tbl,
>> +       .phy_init_tx_tbl_sz     = ARRAY_SIZE(usb3phy_tx_init_tbl),
>> +       .phy_init_rx_tbl        = usb3phy_rx_init_tbl,
>> +       .phy_init_rx_tbl_sz     = ARRAY_SIZE(usb3phy_rx_init_tbl),
>> +       .phy_init_pcs_tbl       = usb3phy_pcs_init_tbl,
>> +       .phy_init_pcs_tbl_sz    = ARRAY_SIZE(usb3phy_pcs_init_tbl),
>> +       .regs                   = usb3phy_regs_layout,
>> +       .mask_start_ctrl        = (PHY_SERDES_START | PHY_PCS_START),
>> +       .mask_pwr_dn_ctrl       = PHY_SW_PWRDN,
>> +};
>> +
>> +static void qcom_qmp_phy_configure(void __iomem *base,
>> +                               unsigned int *regs_layout,
>> +                               struct qmp_phy_init_tbl init_tbl[],
>> +                               int init_tbl_sz)
>> +{
>> +       int i;
>> +
>> +       for (i = 0; i < init_tbl_sz; i++) {
>> +               if (init_tbl[i].in_layout)
>> +                       writel_relaxed(init_tbl[i].cfg_val,
>> +                               base +
>> regs_layout[init_tbl[i].reg_offset]);
>> +               else
>> +                       writel_relaxed(init_tbl[i].cfg_val,
>> +                               base + init_tbl[i].reg_offset);
>> +       }
>> +
>> +       /* flush buffered writes */
>> +       mb();
>> +}
>> +
>> +static int qcom_qmp_phy_poweron(struct phy *phy)
>> +{
>> +       struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
>> +       struct qcom_qmp_phy *qphy = phydesc->qphy;
>> +       int ret;
>> +
>> +       dev_info(&phy->dev, "Powering on QMP phy\n");
>> +
>> +       ret = regulator_enable(qphy->vdda_phy);
>> +       if (ret) {
>> +               dev_err(qphy->dev, "%s: vdda-phy enable failed, err=%d\n",
>> +                               __func__, ret);
>> +               return ret;
>> +       }
>> +
>> +       ret = regulator_enable(qphy->vdda_pll);
>> +       if (ret) {
>> +               dev_err(qphy->dev, "%s: vdda-pll enable failed, err=%d\n",
>> +                               __func__, ret);
>> +               goto err_vdda_pll;
>> +       }
>> +
>> +       if (qphy->vddp_ref_clk) {
>> +               ret = regulator_enable(qphy->vddp_ref_clk);
>> +               if (ret) {
>> +                       dev_err(qphy->dev, "%s: vdda-ref-clk enable
>> failed, err=%d\n",
>> +                                       __func__, ret);
>> +                       goto err_vddp_refclk;
>> +               }
>> +       }
>> +
>> +       if (!qphy->clk_enabled) {
>> +               clk_prepare_enable(qphy->ref_clk_src);
>> +               clk_prepare_enable(qphy->ref_clk);
>> +               clk_prepare_enable(phydesc->pipe_clk);
>> +               qphy->clk_enabled = true;
>> +       }
>> +
>> +       return 0;
>> +
>> +err_vddp_refclk:
>> +       regulator_disable(qphy->vdda_pll);
>> +err_vdda_pll:
>> +       regulator_disable(qphy->vdda_phy);
>> +       return ret;
>> +}
>> +
>> +static int qcom_qmp_phy_poweroff(struct phy *phy)
>> +{
>> +       struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
>> +       struct qcom_qmp_phy *qphy = phydesc->qphy;
>> +
>> +       if (qphy->clk_enabled) {
>> +               clk_disable_unprepare(qphy->ref_clk_src);
>> +               clk_disable_unprepare(qphy->ref_clk);
>> +               clk_disable_unprepare(phydesc->pipe_clk);
>> +               qphy->clk_enabled = false;
>> +       }
>> +
>> +       if (qphy->vddp_ref_clk)
>> +               regulator_disable(qphy->vddp_ref_clk);
>> +
>> +       regulator_disable(qphy->vdda_pll);
>> +       regulator_disable(qphy->vdda_phy);
>> +
>> +       return 0;
>> +}
>> +
>> +static int qcom_qmp_phy_is_ready(struct qcom_qmp_phy *qphy,
>> +                               void __iomem *pcs_status, u32 mask)
>> +{
>> +       unsigned int init_timeout;
>> +
>> +       init_timeout = PHY_READY_TIMEOUT_COUNT;
>> +       do {
>> +               if (readl_relaxed(pcs_status) & mask)
>> +                       break;
>> +
>> +               usleep_range(REFCLK_STABILIZATION_DELAY_US_MIN,
>> +                                REFCLK_STABILIZATION_DELAY_US_MAX);
>> +       } while (--init_timeout);
>> +
>> +       if (!init_timeout)
>> +               return -EBUSY;
>> +
>> +       return 0;
>> +}
>> +
>> +static int qcom_qmp_phy_com_init(struct qcom_qmp_phy *qphy)
>> +{
>> +       const struct qmp_phy_init_cfg *cfg = qphy->cfg;
>> +       void __iomem *serdes = qphy->serdes;
>> +       int ret;
>> +
>> +       mutex_lock(&qphy->phy_mutex);
>> +       if (qphy->init_count++) {
>> +               mutex_unlock(&qphy->phy_mutex);
>> +               return 0;
>> +       }
>> +
>> +       ret = reset_control_deassert(qphy->phy_rst);
>> +       if (ret) {
>> +               dev_err(qphy->dev, "phy reset deassert failed\n");
>
> mutex unlock

Yes, will add one.

>
>> +               return ret;
>> +       }
>> +
>> +       ret = reset_control_deassert(qphy->phycom_rst);
>> +       if (ret) {
>> +               dev_err(qphy->dev, "common reset deassert failed\n");
>> +               goto err_phycom_rst;
>> +       }
>> +
>> +       if (qphy->phycfg_rst) {
>> +               ret = reset_control_deassert(qphy->phycfg_rst);
>> +               if (ret) {
>> +                       dev_err(qphy->dev, "common reset deassert
>> failed\n");
>> +                       goto err_phycfg_rst;
>> +               }
>> +       }
>> +
>> +       if (cfg->has_phy_com_ctrl) {
>> +               qphy_setbits(PHY_SW_PWRDN,
>> +                       serdes + cfg->regs[QPHY_COM_POWER_DOWN_CONTROL]);
>> +               /* Make sure that above write is completed */
>> +               mb();
>> +       }
>> +
>> +       /* Serdes configuration */
>> +       qcom_qmp_phy_configure(serdes, cfg->regs,
>> cfg->phy_init_serdes_tbl,
>> +                               cfg->phy_init_serdes_tbl_sz);
>> +
>> +       if (cfg->has_phy_com_ctrl) {
>> +               qphy_clrbits(PHY_SW_RESET, serdes +
>> +                                       cfg->regs[QPHY_COM_SW_RESET]);
>> +               qphy_setbits(PHY_SERDES_START | PHY_PCS_START,
>> +                               serdes +
>> cfg->regs[QPHY_COM_START_CONTROL]);
>> +               /* Make sure that above write is completed */
>> +               mb();
>> +
>> +               ret = qcom_qmp_phy_is_ready(qphy, serdes +
>> +
>> cfg->regs[QPHY_COM_PCS_READY_STATUS],
>> +                                       MASK_COM_PCS_READY);
>> +               if (ret) {
>> +                       dev_err(qphy->dev,
>> +                               "common control block init timed-out\n");
>> +                       goto err_phy_comctrl;
>> +               }
>> +       }
>> +
>> +       mutex_unlock(&qphy->phy_mutex);
>> +
>> +       return 0;
>> +
>> +err_phy_comctrl:
>
> mutex unlock

Right, here as well.

>
>> +       if (qphy->phycfg_rst)
>> +               reset_control_assert(qphy->phycfg_rst);
>> +err_phycfg_rst:
>> +       reset_control_assert(qphy->phycom_rst);
>> +err_phycom_rst:
>> +       reset_control_assert(qphy->phy_rst);
>> +       return ret;
>> +}
>> +
>> +static int qcom_qmp_phy_com_exit(struct qcom_qmp_phy *qphy)
>> +{
>> +       const struct qmp_phy_init_cfg *cfg = qphy->cfg;
>> +       void __iomem *serdes = qphy->serdes;
>> +
>> +       mutex_lock(&qphy->phy_mutex);
>> +       if (--qphy->init_count) {
>> +               mutex_unlock(&qphy->phy_mutex);
>> +               return 0;
>> +       }
>> +
>> +       if (cfg->has_phy_com_ctrl) {
>> +               qphy_setbits(PHY_SERDES_START | PHY_PCS_START,
>> +                               serdes +
>> cfg->regs[QPHY_COM_START_CONTROL]);
>> +               qphy_clrbits(PHY_SW_RESET, serdes +
>> +                                       cfg->regs[QPHY_COM_SW_RESET]);
>> +               qphy_setbits(PHY_SW_PWRDN,
>> +                       serdes + cfg->regs[QPHY_COM_POWER_DOWN_CONTROL]);
>> +
>> +               /* Make sure that above writes are completed */
>> +               mb();
>> +       }
>> +
>> +       reset_control_assert(qphy->phy_rst);
>> +       reset_control_assert(qphy->phycom_rst);
>> +       if (qphy->phycfg_rst)
>> +               reset_control_assert(qphy->phycfg_rst);
>> +
>> +       mutex_unlock(&qphy->phy_mutex);
>> +
>> +       return 0;
>> +}
>> +
>> +/* PHY Initialization */
>> +static int qcom_qmp_phy_init(struct phy *phy)
>> +{
>> +       struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
>> +       struct qcom_qmp_phy *qphy = phydesc->qphy;
>> +       const struct qmp_phy_init_cfg *cfg = qphy->cfg;
>> +       void __iomem *tx = phydesc->tx;
>> +       void __iomem *rx = phydesc->rx;
>> +       void __iomem *pcs = phydesc->pcs;
>> +       int ret;
>> +
>> +       dev_info(qphy->dev, "Initializing QMP phy\n");
>> +
>> +       /* enable interface clocks to program phy */
>> +       clk_prepare_enable(qphy->aux_clk);
>> +       clk_prepare_enable(qphy->cfg_ahb_clk);
>> +
>> +       ret = qcom_qmp_phy_com_init(qphy);
>> +       if (ret)
>> +               goto err;
>> +
>> +       if (phydesc->lane_rst) {
>> +               ret = reset_control_deassert(phydesc->lane_rst);
>> +               if (ret) {
>> +                       dev_err(qphy->dev, "lane<%d> reset deassert
>> failed\n",
>> +                                       phydesc->index);
>> +                       goto err_lane_rst;
>> +               }
>> +       }
>> +
>> +       /* Tx, Rx, and PCS configurations */
>> +       qcom_qmp_phy_configure(tx, cfg->regs, cfg->phy_init_tx_tbl,
>> +                               cfg->phy_init_tx_tbl_sz);
>> +       qcom_qmp_phy_configure(rx, cfg->regs, cfg->phy_init_rx_tbl,
>> +                               cfg->phy_init_rx_tbl_sz);
>> +       qcom_qmp_phy_configure(pcs, cfg->regs, cfg->phy_init_pcs_tbl,
>> +                               cfg->phy_init_pcs_tbl_sz);
>> +
>> +       /*
>> +        * Pull out PHY from POWER DOWN state:
>> +        * This is active low enable signal to power-down PHY.
>> +        */
>> +       qphy_setbits(cfg->mask_pwr_dn_ctrl,
>> +                       pcs + QPHY_POWER_DOWN_CONTROL);
>> +       /* XXX: 10 us delay; given in PCIE HPG only */
>> +       usleep_range(POWER_DOWN_DELAY_US_MIN, POWER_DOWN_DELAY_US_MAX);
>> +
>> +       /* start SerDes and Phy-Coding-Sublayer */
>> +       qphy_setbits(cfg->mask_start_ctrl, pcs + QPHY_START_CTRL);
>> +
>> +       /* Pull PHY out of reset state */
>> +       qphy_clrbits(PHY_SW_RESET, pcs + QPHY_SW_RESET);
>> +       /* Make sure that above writes are completed */
>> +       mb();
>> +
>> +       ret = qcom_qmp_phy_is_ready(qphy, pcs +
>> +                                       cfg->regs[QPHY_PCS_READY_STATUS],
>> +                                       MASK_PHYSTATUS);
>> +       if (ret) {
>> +               dev_err(qphy->dev, "phy initialization timed-out\n");
>> +               goto err_pcs_ready;
>> +       }
>> +
>> +       return 0;
>> +
>> +err_pcs_ready:
>> +       if (phydesc->lane_rst)
>> +               reset_control_assert(phydesc->lane_rst);
>> +err_lane_rst:
>> +       qcom_qmp_phy_com_exit(qphy);
>> +err:
>> +       clk_disable_unprepare(qphy->cfg_ahb_clk);
>> +       clk_disable_unprepare(qphy->aux_clk);
>> +       return ret;
>> +}
>> +
>> +static int qcom_qmp_phy_exit(struct phy *phy)
>> +{
>> +       struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
>> +       struct qcom_qmp_phy *qphy = phydesc->qphy;
>> +       const struct qmp_phy_init_cfg *cfg = qphy->cfg;
>> +
>> +       /* PHY reset */
>> +       qphy_setbits(PHY_SW_RESET, phydesc->pcs + QPHY_SW_RESET);
>> +
>> +       /* stop SerDes and Phy-Coding-Sublayer */
>> +       qphy_clrbits(cfg->mask_start_ctrl, phydesc->pcs +
>> QPHY_START_CTRL);
>> +
>> +       /* Put PHY into POWER DOWN state: active low */
>> +       qphy_clrbits(cfg->mask_pwr_dn_ctrl,
>> +                       phydesc->pcs + QPHY_POWER_DOWN_CONTROL);
>> +
>> +       /* Make sure that above writes are completed */
>> +       mb();
>> +
>> +       if (phydesc->lane_rst)
>> +               reset_control_assert(phydesc->lane_rst);
>> +
>> +       qcom_qmp_phy_com_exit(qphy);
>> +
>> +       clk_disable_unprepare(qphy->aux_clk);
>> +       clk_disable_unprepare(qphy->cfg_ahb_clk);
>> +
>> +       return 0;
>> +}
>> +
>> +
>> +static int qcom_qmp_phy_regulator_init(struct device *dev)
>> +{
>> +       struct qcom_qmp_phy *qphy = dev_get_drvdata(dev);
>> +       int ret = 0;
>> +
>> +       qphy->vdda_phy = devm_regulator_get(dev, "vdda-phy");
>> +       if (IS_ERR(qphy->vdda_phy)) {
>> +               ret = PTR_ERR(qphy->vdda_phy);
>> +               dev_err(dev, "failed to get vdda-phy, %d\n", ret);
>> +               return ret;
>> +       }
>> +
>> +       qphy->vdda_pll = devm_regulator_get(dev, "vdda-pll");
>> +       if (IS_ERR(qphy->vdda_pll)) {
>> +               ret = PTR_ERR(qphy->vdda_pll);
>> +               dev_err(dev, "failed to get vdda-pll, %d\n", ret);
>> +               return ret;
>> +       }
>> +
>> +       /* optional regulator */
>> +       qphy->vddp_ref_clk = devm_regulator_get(dev, "vddp-ref-clk");
>
>
> Should mention this in bindings too.

Right, missed this one. Will add.

>
>
>> +       if (IS_ERR(qphy->vddp_ref_clk)) {
>> +               ret = PTR_ERR(qphy->vddp_ref_clk);
>> +               dev_info(dev, "failed to get vddp-ref-clk, %d\n", ret);
>> +               qphy->vddp_ref_clk = NULL;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static int qcom_qmp_phy_clk_init(struct device *dev)
>> +{
>> +       struct qcom_qmp_phy *qphy = dev_get_drvdata(dev);
>> +       int ret;
>> +
>> +       qphy->aux_clk = devm_clk_get(dev, "aux");
>> +       if (IS_ERR(qphy->aux_clk)) {
>> +               ret = PTR_ERR(qphy->aux_clk);
>> +               if (ret != -EPROBE_DEFER)
>> +                       dev_err(dev, "failed to get aux_clk\n");
>> +               return ret;
>> +       }
>> +
>> +       qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb");
>> +       if (IS_ERR(qphy->cfg_ahb_clk)) {
>> +               ret = PTR_ERR(qphy->cfg_ahb_clk);
>> +               if (ret != -EPROBE_DEFER)
>> +                       dev_err(dev, "failed to get cfg_ahb_clk\n");
>> +               return ret;
>> +       }
>> +
>> +       /*
>> +        * ref_clk and ref_clk_src handles may not be available in
>> +        * all hardwares. So we don't return error in these cases.
>> +        */
>
>
> This thing has to be mentioned in the bindings too.

 All current chips have and future chips will have these clocks available
with them. So, I think we can make these clocks mandatory (not Optional).

>
>
>> +       qphy->ref_clk_src = devm_clk_get(dev, "ref_clk_src");
>> +       if (IS_ERR(qphy->ref_clk_src)) {
>> +               ret = PTR_ERR(qphy->ref_clk_src);
>> +               if (ret != -EPROBE_DEFER) {
>> +                       qphy->ref_clk_src = NULL;
>> +                       dev_dbg(dev, "failed to get ref_clk_src\n");
>> +               } else {
>> +                       return ret;
>> +               }
>> +       }
>> +
>> +       qphy->ref_clk = devm_clk_get(dev, "ref_clk");
>> +       if (IS_ERR(qphy->ref_clk)) {
>> +               ret = PTR_ERR(qphy->ref_clk);
>> +               if (ret != -EPROBE_DEFER) {
>> +                       qphy->ref_clk = NULL;
>> +                       dev_dbg(dev, "failed to get ref_clk\n");
>> +               } else {
>> +                       return ret;
>> +               }
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static struct phy *qcom_qmp_phy_xlate(struct device *dev,
>> +                                       struct of_phandle_args *args)
>> +{
>> +       struct qcom_qmp_phy *qphy = dev_get_drvdata(dev);
>> +       int i;
>> +
>> +       if (WARN_ON(args->args[0] >= qphy->cfg->nlanes))
>> +               return ERR_PTR(-ENODEV);
>> +
>> +       for (i = 0; i < qphy->cfg->nlanes; i++) {
>> +               if (qphy->phys[i]->index == args->args[0])
>> +                       break;
>> +       }
>> +
>> +       if (i == qphy->cfg->nlanes)
>> +               return ERR_PTR(-ENODEV);
>> +
>> +       return qphy->phys[i]->phy;
>> +}
>> +
>> +static const struct phy_ops qcom_qmp_phy_gen_ops = {
>> +       .init           = qcom_qmp_phy_init,
>> +       .exit           = qcom_qmp_phy_exit,
>> +       .power_on       = qcom_qmp_phy_poweron,
>> +       .power_off      = qcom_qmp_phy_poweroff,
>> +       .owner          = THIS_MODULE,
>> +};
>> +
>> +static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
>> +       {
>> +               .compatible = "qcom,msm8996-qmp-pcie-phy",
>> +               .data = &pciephy_init_cfg,
>> +       }, {
>> +               .compatible = "qcom,msm8996-qmp-usb3-phy",
>> +               .data = &usb3phy_init_cfg,
>> +       },
>> +       { },
>> +};
>> +MODULE_DEVICE_TABLE(of, qcom_qmp_phy_of_match_table);
>> +
>> +static int qcom_qmp_phy_probe(struct platform_device *pdev)
>> +{
>> +       struct qcom_qmp_phy *qphy;
>> +       struct device *dev = &pdev->dev;
>> +       struct device_node *child;
>> +       struct phy_provider *phy_provider;
>> +       struct resource *res;
>> +       const struct of_device_id *match;
>> +       void __iomem *base;
>> +       int ret = 0;
>> +       int id;
>> +
>> +       qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
>> +       if (!qphy)
>> +               return -ENOMEM;
>
> new line

Ok.

>>
>> +       qphy->dev = dev;
>> +       dev_set_drvdata(dev, qphy);
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       if (!res)
>> +               return -ENODEV;
>
> new line

Sure,

>
>> +       base = devm_ioremap_resource(dev, res);
>> +       if (IS_ERR(base))
>> +               return PTR_ERR(base);
>> +
>> +       /* per PHY serdes; usually located at base address */
>> +       qphy->serdes = base;
>> +
>> +       mutex_init(&qphy->phy_mutex);
>> +
>> +       /* Get the specific init parameters of QMP phy */
>> +       match = of_match_node(qcom_qmp_phy_of_match_table, dev->of_node);
>> +       qphy->cfg = match->data;
>> +
>> +       ret = qcom_qmp_phy_clk_init(dev);
>> +       if (ret) {
>> +               dev_err(dev, "clock init failed\n");
>> +               return ret;
>> +       }
>> +
>> +       ret = qcom_qmp_phy_regulator_init(dev);
>> +       if (ret) {
>> +               dev_err(dev, "regulator init failed\n");
>> +               return ret;
>> +       }
>> +
>> +       qphy->phy_rst = devm_reset_control_get(dev, "phy");
>> +       if (IS_ERR(qphy->phy_rst)) {
>> +               dev_err(dev, "failed to get phy core reset\n");
>> +               return PTR_ERR(qphy->phy_rst);
>> +       }
>> +
>> +       qphy->phycom_rst = devm_reset_control_get(dev, "common");
>> +       if (IS_ERR(qphy->phycom_rst)) {
>> +               dev_err(dev, "failed to get phy common reset\n");
>> +               return PTR_ERR(qphy->phycom_rst);
>> +       }
>> +
>> +       qphy->phycfg_rst = devm_reset_control_get(dev, "cfg");
>> +       if (IS_ERR(qphy->phycfg_rst)) {
>> +               dev_err(dev, "failed to get phy ahb cfg reset\n");
>> +               qphy->phycfg_rst = NULL;
>> +       }
>> +
>> +       qphy->phys = devm_kcalloc(dev, qphy->cfg->nlanes,
>> +                                       sizeof(*qphy->phys), GFP_KERNEL);
>> +       if (!qphy->phys)
>> +               return -ENOMEM;
>> +
>> +       for (id = 0; id < qphy->cfg->nlanes; id++) {
>> +               struct phy *generic_phy;
>> +               struct qmp_phy_desc *phy_desc;
>> +               char prop_name[MAX_PROP_NAME];
>> +
>> +               phy_desc = devm_kzalloc(dev, sizeof(*phy_desc),
>> GFP_KERNEL);
>> +               if (!phy_desc) {
>> +                       ret = -ENOMEM;
>> +                       goto put_child;
>> +               }
>> +
>> +               phy_desc->tx = base + qphy->cfg->tx_offsets[id];
>> +               phy_desc->rx = base + qphy->cfg->rx_offsets[id];
>> +               phy_desc->pcs = base + qphy->cfg->pcs_offsets[id];
>> +
>> +               /*
>> +                * Get PHY's Pipe clock, if any; USB3 and PCIe are PIPE
>> +                * based phys, so they essentially have pipe clock
>> +                */
>> +               memset(&prop_name, 0, sizeof(prop_name));
>> +               snprintf(prop_name, MAX_PROP_NAME, "pipe%d", id);
>> +               phy_desc->pipe_clk = devm_clk_get(dev, prop_name);
>> +               if (IS_ERR(phy_desc->pipe_clk)) {
>> +                       if (qphy->cfg->type == PHY_TYPE_PCIE ||
>> +                           qphy->cfg->type == PHY_TYPE_USB3) {
>> +                               ret = PTR_ERR(phy_desc->pipe_clk);
>> +                               if (ret != -EPROBE_DEFER)
>> +                                       dev_err(dev,
>> +                                       "failed to get lane%d pipe_clk\n",
>> id);
>> +                               return ret;
>> +                       } else {
>> +                               phy_desc->pipe_clk = NULL;
>> +                       }
>> +               }
>
>
> Do we need to really need phy type for this?
> You should proably make a flag in cfg to mark this rather then using phy
> type. Hopefully it will also simplify the logic.

The pipe clock is something that's specific to PCIe and USB3 type
phys (both are PIPE3 specs based phys). So i think it is okay
to use the PHY_TYPE_{*} macros here. Looks a bit cleaner rather
than having a cfg flag. No ?

Using PHY_TYPE_{*} macros will also keep the doors open to
other PHY types that are added at a later point in time.

>
>
>> +
>> +               /* Get lane reset, if any */
>> +               memset(&prop_name, 0, sizeof(prop_name));
>> +               snprintf(prop_name, MAX_PROP_NAME, "lane%d", id);
>> +               phy_desc->lane_rst = devm_reset_control_get(dev,
>> prop_name);
>> +               if (IS_ERR(phy_desc->lane_rst)) {
>> +                       if (qphy->cfg->type == PHY_TYPE_PCIE) {
>> +                               dev_err(dev, "failed to get lane%d
>> reset\n",
>> +
>> id);
>> +                               ret = PTR_ERR(phy_desc->lane_rst);
>> +                               goto put_child;
>> +                       } else {
>> +                               phy_desc->lane_rst = NULL;
>> +                       }
>> +               }
>
>
> Same comment as above.

Correct, here we can add the cfg flag as you suggested.

>
>
>
>> +
>> +               generic_phy = devm_phy_create(dev, NULL,
>> &qcom_qmp_phy_gen_ops);
>> +               if (IS_ERR(generic_phy)) {
>> +                       ret = PTR_ERR(generic_phy);
>> +                       dev_err(dev, "failed to create qphy %d\n", ret);
>> +                       goto put_child;
>> +               }
>> +
>> +               phy_desc->phy = generic_phy;
>> +               phy_desc->index = id;
>> +               phy_desc->qphy = qphy;
>> +               phy_set_drvdata(generic_phy, phy_desc);
>> +               qphy->phys[id] = phy_desc;
>> +       }
>> +
>> +       phy_provider = devm_of_phy_provider_register(dev,
>> qcom_qmp_phy_xlate);
>> +       if (IS_ERR(phy_provider)) {
>> +               ret = PTR_ERR(phy_provider);
>> +               dev_err(dev, "failed to register qphy %d\n", ret);
>> +               goto put_child;
>> +       }
>> +
>> +put_child:
>> +       of_node_put(child);
>> +       return ret;
>> +}
>> +
>> +static struct platform_driver qcom_qmp_phy_driver = {
>> +       .probe          = qcom_qmp_phy_probe,
>> +       .driver = {
>> +               .name   = "qcom_qmp_phy",
>> +               .of_match_table =
>> of_match_ptr(qcom_qmp_phy_of_match_table),
>> +       },
>> +};
>> +
>> +module_platform_driver(qcom_qmp_phy_driver);
>> +
>> +MODULE_AUTHOR("Vivek Gautam <vivek.gautam-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>");
>> +MODULE_DESCRIPTION("Qualcomm QMP PHY driver");
>> +MODULE_LICENSE("GPL v2");
>>
>


Thanks
Vivek

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] phy: qcom-qmp: new qmp phy driver for qcom-chipsets
  2016-10-26 21:18     ` Rob Herring
@ 2016-11-02  7:27       ` Vivek Gautam
  0 siblings, 0 replies; 19+ messages in thread
From: Vivek Gautam @ 2016-11-02  7:27 UTC (permalink / raw)
  To: Rob Herring
  Cc: kishon, Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA

On Thu, Oct 27, 2016 at 2:48 AM, Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> On Wed, Oct 19, 2016 at 04:13:47PM +0530, Vivek Gautam wrote:
>> Qualcomm SOCs have QMP phy controller that provides support
>> to a number of controller, viz. PCIe, UFS, and USB.
>> Add a new driver, based on generic phy framework, for this
>> phy controller.
>>
>> USB3-phy changes: Based on phy-msm-ssusb-qmp driver available on
>> msm-4.4 kernel @codeaurora[1].
>> PCIe-phy changes: Based on msm8996-pcie-phy driver posted by
>> Srinivas [2].
>>
>> [1] https://source.codeaurora.org/quic/la/kernel/msm-4.4/log/?h=caf/3.18/msm-3.18
>> [2] https://patchwork.kernel.org/patch/9318947/
>>
>> Signed-off-by: Vivek Gautam <vivek.gautam-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
>> Cc: Kishon Vijay Abraham I <kishon-l0cyMroinI0@public.gmane.org>
>> ---
>>  .../devicetree/bindings/phy/qcom-qmp-phy.txt       |   61 ++
>
> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

Thanks Rob.


Regards
Vivek

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] phy: qcom-qusb2: New driver for QUSB2 PHY on Qcom chips
  2016-10-26 21:16   ` Rob Herring
@ 2016-11-02  8:11     ` Vivek Gautam
  0 siblings, 0 replies; 19+ messages in thread
From: Vivek Gautam @ 2016-11-02  8:11 UTC (permalink / raw)
  To: Rob Herring
  Cc: kishon, Mark Rutland, devicetree, linux-kernel, linux-arm-msm,
	Srinivas Kandagatla

Hi Rob,


On Thu, Oct 27, 2016 at 2:46 AM, Rob Herring <robh@kernel.org> wrote:
> On Wed, Oct 19, 2016 at 04:13:46PM +0530, Vivek Gautam wrote:
>> PHY transceiver driver for QUSB2 phy controller that provides
>> HighSpeed functionality for DWC3 controller present on
>> Qualcomm chipsets.
>>
>> This driver is based on phy-msm-qusb driver available in
>> msm-4.4 kernel @codeaurora[1]
>>
>> [1] https://source.codeaurora.org/quic/la/kernel/msm-4.4/log/?h=caf/3.18/msm-3.18
>>
>> Signed-off-by: Vivek Gautam <vivek.gautam@codeaurora.org>
>> Cc: Kishon Vijay Abraham I <kishon@ti.com>
>> ---
>>  .../devicetree/bindings/phy/qcom-qusb2-phy.txt     |  37 ++
>
> It's preferred the bindings are a separate patch.

Sure, will prepare a separate patch for bindings doc for both the phy patches.

>
>>  drivers/phy/Kconfig                                |  10 +
>>  drivers/phy/Makefile                               |   1 +
>>  drivers/phy/phy-qcom-qusb2.c                       | 577 +++++++++++++++++++++
>>  4 files changed, 625 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt
>>  create mode 100644 drivers/phy/phy-qcom-qusb2.c
>>
>> diff --git a/Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt b/Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt
>> new file mode 100644
>> index 0000000..97c9ce7
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt
>> @@ -0,0 +1,37 @@
>> +Qualcomm QUSB2 phy controller
>> +=============================
>> +
>> +QUSB2 controller supports LS/FS/HS usb connectivity on Qualcomm chipsets.
>> +
>> +Required properties:
>> + - compatible: compatible list, contains "qcom,msm8996-qusb2-phy".
>> + - reg: offset and length of the PHY register set.
>> + - #phy-cells: must be 0.
>> +
>> + - clocks: a list of phandles and clock-specifier pairs,
>> +        one for each entry in clock-names.
>> + - clock-names: must be "cfg_ahb" for phy config clock,
>> +                     "ref_clk" for 19.2 MHz ref clk,
>> +                     "ref_clk_src" reference clock source.
>> +                     "iface" for phy interface clock (Optional).
>> +
>> + - vdd-phy-supply: Phandle to a regulator supply to PHY core block.
>> + - vdda-pll-supply: Phandle to 1.8V regulator supply to PHY refclk pll block.
>> + - vdda-phy-dpdm: Phandle to 3.1V regulator supply to Dp/Dm port signals.
>> +
>> + - resets: a list of phandles and reset controller specifier pairs,
>> +        one for each entry in reset-names.
>> + - reset-names: must be "phy" for reset of phy block.
>> +
>> +Optional properties:
>> + - nvmem-cells: a list of phandles to nvmem cells that contain fused
>> +             tuning parameters for qusb2 phy, one for each entry
>> +             in nvmem-cell-names.
>> + - nvmem-cell-names: must be "tune2_hstx_trim_efuse" for cell containing
>> +                  HS Tx trim value.
>> + - qcom,hstx-trim-bit-offset: bit offset within nvmem cell for
>> +                           HS Tx trim value.
>> + - qcom,hstx-trim-bit-len: bit length of HS Tx trim value within nvmem cell.
>
> When does this change? Why is it not just a different nvmem cell?

This is per-controller bit fields. So for example, msm8996 that
has couple of controllers, has these bits as a part of same
cell of 32bits. Bits 21:24 for controller-1, and 25-28 for controller-2.

I thought of using the 'reg' and 'bit' properties of nvmem cell,
but was unsuccessful in using the same.

Hi Srini,
Can you please help me with this ? We have one register of 32 bits
(representing one nvmem cell), that has the value for both the
QUSB2 controllers. So, we just need to read the values that are placed
in two different nibbles, for the two controllers, using nvmem cell
device tree properties (may be 'reg' and 'bit' properties).


Thanks
Vivek

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] phy: qcom-qusb2: New driver for QUSB2 PHY on Qcom chips
  2016-10-26 19:56   ` Kishon Vijay Abraham I
@ 2016-11-02  8:59     ` Vivek Gautam
  0 siblings, 0 replies; 19+ messages in thread
From: Vivek Gautam @ 2016-11-02  8:59 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: robh+dt, Mark Rutland, devicetree, linux-kernel, linux-arm-msm

Hi Kishon,


On Thu, Oct 27, 2016 at 1:26 AM, Kishon Vijay Abraham I <kishon@ti.com> wrote:
> Hi,

I am currently on vacation, so the responses are delayed a bit.
Please bear with me.

>
> On Wednesday 19 October 2016 04:13 PM, Vivek Gautam wrote:
>> PHY transceiver driver for QUSB2 phy controller that provides
>> HighSpeed functionality for DWC3 controller present on
>> Qualcomm chipsets.
>>
>> This driver is based on phy-msm-qusb driver available in
>> msm-4.4 kernel @codeaurora[1]
>>
>> [1] https://source.codeaurora.org/quic/la/kernel/msm-4.4/log/?h=caf/3.18/msm-3.18
>
> I'd prefer only LKML links in commit msg. This can be part of the comment
> section below ---.

Sure, will use LKML links only.

>>
>> Signed-off-by: Vivek Gautam <vivek.gautam@codeaurora.org>
>> Cc: Kishon Vijay Abraham I <kishon@ti.com>
>> ---
>>  .../devicetree/bindings/phy/qcom-qusb2-phy.txt     |  37 ++
>>  drivers/phy/Kconfig                                |  10 +
>>  drivers/phy/Makefile                               |   1 +
>>  drivers/phy/phy-qcom-qusb2.c                       | 577 +++++++++++++++++++++
>>  4 files changed, 625 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt
>>  create mode 100644 drivers/phy/phy-qcom-qusb2.c

[snip]

>> +struct qusb2_phy_init_tbl {
>> +     unsigned int reg_offset;
>> +     unsigned int cfg_val;
>> +};
>> +#define QCOM_QUSB2_PHY_INIT_CFG(reg, val) \
>> +     {                               \
>> +             .reg_offset = reg,      \
>> +             .cfg_val = val,         \
>> +     }
>> +
>> +static struct qusb2_phy_init_tbl msm8996_phy_init_tbl[] = {
>> +     QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE1, 0xF8),
>> +     QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE2, 0xB3),
>> +     QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE3, 0x83),
>> +     QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE4, 0xC0),
>> +     QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30),
>> +     QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79),
>> +     QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21),
>> +     QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TEST2, 0x14),
>> +     QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9F),
>> +     QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00),
>> +};
> we should generalize this so that it could be used for any phys. All these
> tuning parameters can come from dt.
>
> phy,tx-swing = <reg_offset> <swing value>;
> phy,rx-swing = <reg_offset> <swing value>;
> phy,tx-compensation = <reg_offset> <swing value>;
> phy,rx-compensation = <reg_offset> <swing value>;
> ..
>
> We have to identify all the calibration parameters and add a dt property for
> each parameter. The phy-core can just use these properties to calibrate or tune
> the PHY.

Yes, that will make life easier.

Things look simpler with this qusb2 phy driver.
But, same is not the case with the qmp-phy driver
that supports few other phys on msm chipsets.
That qmp phy driver has a list of ~100 odd registers that
need to be programmed during initialization.
We, then, don't need to touch those settings any time later.
The settings include, the common PLL settings, tx, rx and the
pcs settings.

I will take a look at these drivers once again, and see if we can group
the settings and make few generic bindings that can be utilized
but the phy drivers in general.

>
>> +
>> +struct qusb2_phy_init_cfg {
>> +     struct qusb2_phy_init_tbl *phy_init_tbl;
>> +     int phy_init_tbl_sz;
>> +};
>> +
>> +const struct qusb2_phy_init_cfg msm8996_phy_init_cfg = {
>> +     .phy_init_tbl = msm8996_phy_init_tbl,
>> +     .phy_init_tbl_sz = ARRAY_SIZE(msm8996_phy_init_tbl),
>> +};
>> +
>> +/**
>> + * struct qusb2_phy: Structure holding qusb2 phy attributes.
>> + *
>> + * @phy: pointer to generic phy.
>> + * @base: pointer to iomapped memory space for qubs2 phy.
>> + *
>> + * @cfg_ahb_clk: pointer to AHB2PHY interface clock.
>> + * @ref_clk: pointer to reference clock.
>> + * @ref_clk_src: pointer to source to reference clock.
>> + * @iface_src: pointer to phy interface clock.
>> + * @clk_enabled: check if clocks are enabled.
>
> Why is this needed? IMO this should not be used. clk framework can handle this
> better since it maintains a reference count for clk_enable/clk_disable.

Ok, will remove this.

>> + *
>> + * @phy_reset: Pointer to phy reset control
>> + *
>> + * @vdda_phy: vdd supply to the phy core block.
>> + * @vdda_pll: 1.8V vdd supply to ref_clk block.
>> + * @vdda_phy_dpdm: 3.1V vdd supply to Dp/Dm port signals.
>> + * @pwr_enabled: check if the regulators are enabled.
>
> same here.

will remove this as well.

>> + * @tcsr: pointer to TCSR syscon register map.
>> + * @clk_scheme_offset: offset to PHY_CLK_SCHEME register in TCSR map.
>> + * @cfg: phy initialization config data
>> + */
>> +struct qusb2_phy {
>> +     struct phy *phy;
>> +     void __iomem *base;
>> +
>> +     struct clk *cfg_ahb_clk;
>> +     struct clk *ref_clk;
>> +     struct clk *ref_clk_src;
>> +     struct clk *iface_clk;
>> +     bool clk_enabled;
>> +
>> +     struct reset_control *phy_reset;
>> +
>> +     struct regulator *vdd_phy;
>> +     struct regulator *vdda_pll;
>> +     struct regulator *vdda_phy_dpdm;
>> +     bool pwr_enabled;
>> +
>> +     struct regmap *tcsr;
>> +     unsigned int clk_scheme_offset;
>> +
>> +     const struct qusb2_phy_init_cfg *cfg;
>> +};
>> +
>> +static inline void qusb2_set_bits(void __iomem *reg, u32 mask)
>
> mask is misleading. just use val.

Ok, will modify this.

>> +{
>> +     u32 val;
>> +
>> +     val = readl_relaxed(reg);
>> +     val |= mask;
>> +     writel_relaxed(val, reg);
>> +
>> +     /* Ensure above write is completed */
>> +     mb();
>> +}
>> +
>> +static inline void qusb2_clr_bits(void __iomem *reg, u32 mask)
>> +{
>> +     u32 val;
>> +
>> +     val = readl_relaxed(reg);
>> +     val &= ~mask;
>> +     writel_relaxed(val, reg);
>> +
>> +     /* Ensure above write is completed */
>> +     mb();
>> +}
>> +
>> +static void qcom_qusb2_phy_configure(void __iomem *base,
>> +                             struct qusb2_phy_init_tbl init_tbl[],
>> +                             int init_tbl_sz)
>> +{
>> +     int i;
>> +
>> +     for (i = 0; i < init_tbl_sz; i++) {
>> +             writel_relaxed(init_tbl[i].cfg_val,
>> +                             base + init_tbl[i].reg_offset);
>> +     }
>> +
>> +     /* flush buffered writes */
>> +     mb();
>> +}
>> +
>> +static void qusb2_phy_enable_clocks(struct qusb2_phy *qphy, bool on)
>> +{
>> +     dev_dbg(&qphy->phy->dev, "%s(): clocks_enabled:%d on:%d\n",
>> +                     __func__, qphy->clk_enabled, on);
>
> unnecessary debug print IMO, given that there is another debug print before the
> function returns.

Will remove this.

>> +
>> +     if (!qphy->clk_enabled && on) {
>> +             clk_prepare_enable(qphy->cfg_ahb_clk);
>> +             if (qphy->iface_clk)
>> +                     clk_prepare_enable(qphy->iface_clk);
>> +             clk_prepare_enable(qphy->ref_clk_src);
>> +             qphy->clk_enabled = true;
>> +     }
>> +
>> +     if (qphy->clk_enabled && !on) {
>> +             clk_disable_unprepare(qphy->ref_clk_src);
>> +             if (qphy->iface_clk)
>> +                     clk_disable_unprepare(qphy->iface_clk);
>> +             clk_disable_unprepare(qphy->cfg_ahb_clk);
>> +             qphy->clk_enabled = false;
>> +     }
>> +
>> +     dev_dbg(&qphy->phy->dev, "%s(): clocks_enabled:%d\n", __func__,
>> +                                             qphy->clk_enabled);
>
> even this should be dev_vdbg.

Ok, will use dev_vdbg.

>> +}
>> +
>> +static int qusb2_phy_enable_power(struct qusb2_phy *qphy, bool on)
>> +{
>> +     int ret = 0;
>> +     struct device *dev = &qphy->phy->dev;
>> +
>> +     dev_dbg(dev, "%s(): turn %s regulators. power_enabled:%d\n",
>> +                     __func__, on ? "on" : "off", qphy->pwr_enabled);
>> +
>> +     if (qphy->pwr_enabled == on)
>> +             return 0;
>> +
>> +     if (!on)
>> +             goto disable_vdda_phy_dpdm;
>> +
>> +     ret = regulator_enable(qphy->vdd_phy);
>> +     if (ret) {
>> +             dev_err(dev, "Unable to enable vdd-phy:%d\n", ret);
>> +             goto err_vdd_phy;
>> +     }
>> +
>> +     ret = regulator_enable(qphy->vdda_pll);
>> +     if (ret) {
>> +             dev_err(dev, "Unable to enable vdda-pll:%d\n", ret);
>> +             goto disable_vdd_phy;
>> +     }
>> +
>> +     ret = regulator_enable(qphy->vdda_phy_dpdm);
>> +     if (ret) {
>> +             dev_err(dev, "Unable to enable vdda-phy-dpdm:%d\n", ret);
>> +             goto disable_vdda_pll;
>> +     }
>> +
>> +     qphy->pwr_enabled = true;
>> +     dev_dbg(dev, "%s() regulators are turned on.\n", __func__);
>> +
>> +     return ret;
>> +
>> +disable_vdda_phy_dpdm:
>> +     regulator_disable(qphy->vdda_phy_dpdm);
>> +disable_vdda_pll:
>> +     regulator_disable(qphy->vdda_pll);
>> +disable_vdd_phy:
>> +     regulator_disable(qphy->vdd_phy);
>> +err_vdd_phy:
>> +     qphy->pwr_enabled = false;
>> +     dev_dbg(dev, "%s() regulators are turned off.\n", __func__);
>> +     return ret;
>> +}
>> +
>> +/*
>> + * Fetches HS Tx tuning value from e-fuse and sets QUSB2PHY_PORT_TUNE2
>> + * register.
>> + * For any error case, skip setting the value and use the default value.
>> + */
>> +static int qusb2_phy_set_tune2_param(struct qusb2_phy *qphy)
>> +{
>> +     struct device *dev = &qphy->phy->dev;
>> +     struct nvmem_cell *cell;
>> +     unsigned int bit_mask;
>> +     u32 tune2_val;
>> +     int bit_offset = 0;
>> +     int bit_len = 0;
>> +     ssize_t len;
>> +     u32 *val;
>> +     int ret;
>> +
>> +     /*
>> +      * Read EFUSE register having TUNE2 parameter's high nibble.
>> +      * If efuse register shows value as 0x0, or if we fail to find
>> +      * a valid efuse register settings, then use default value
>> +      * as 0xB for high nibble that we have already set while
>> +      * configuring phy.
>> +      */
>> +     cell = devm_nvmem_cell_get(dev, "tune2_hstx_trim_efuse");
>> +     if (IS_ERR(cell)) {
>> +             if (PTR_ERR(cell) == -EPROBE_DEFER)
>> +                     return PTR_ERR(cell);
>> +             goto skip;
>> +     }
>> +
>> +     val = (u32 *)nvmem_cell_read(cell, &len);
>> +     if (!IS_ERR(val)) {
>> +             ret = of_property_read_u32(dev->of_node,
>> +                                             "qcom,hstx-trim-bit-offset",
>> +                                             &bit_offset);
>> +             if (ret) {
>> +                     dev_dbg(dev, "hstx-trim bit offset is invalid.\n");
>> +                     goto skip;
>> +             }
>> +
>> +             ret = of_property_read_u32(dev->of_node,
>> +                                             "qcom,hstx-trim-bit-len",
>> +                                             &bit_len);
>> +             if (ret) {
>> +                     dev_dbg(dev, "hstx-trim bit length is invalid.\n");
>> +                     goto skip;
>> +             }
>> +
>> +             bit_mask = (1 << bit_len) - 1;
>> +             dev_dbg(dev,
>> +                     "%s(): efuse value:%x, bit_mask:%x, bit_offset: %d\n",
>> +                     __func__, val[0], bit_mask, bit_offset);
>> +             tune2_val = TUNE2_HIGH_NIBBLE_VAL(val[0], bit_offset, bit_mask);
>> +             if (!tune2_val) {
>> +                     dev_dbg(dev, "hstx-trim bit length is invalid.\n");
>> +                     goto skip;
>> +             }
>> +
>> +             /* Fused TUNE2 value is the higher nibble only */
>> +             tune2_val = tune2_val << 0x4;
>> +             qusb2_set_bits(qphy->base + QUSB2PHY_PORT_TUNE2, tune2_val);
>> +     } else {
>> +             dev_dbg(dev, "failed reading hs-tx trim value: %d\n", ret);
>> +     }
>> +
>> +skip:
>> +     return 0;
>> +}
>> +
>> +static int qusb2_phy_init(struct phy *phy)
>> +{
>> +     struct qusb2_phy *qphy = phy_get_drvdata(phy);
>> +     unsigned int reset_val;
>> +     unsigned int clk_scheme;
>> +     bool is_se_clk = false;
>> +     int ret;
>> +
>> +     dev_info(&phy->dev, "Initializing QUSB2 phy\n");
>
> dev_vdbg here..

okay.

>> +
>> +     ret = qusb2_phy_enable_power(qphy, true);
>> +     if (ret)
>> +             return ret;
>
> shouldn't this be done in power_on?

Okay, will add power_on/off() callbacks and move this code there.

>> +
>> +     qusb2_phy_enable_clocks(qphy, true);
>> +
>> +     /* Perform phy reset */
>> +     ret = reset_control_assert(qphy->phy_reset);
>> +     if (ret) {
>> +             dev_err(&phy->dev, "Failed to assert phy_reset\n");
>> +             return ret;
>> +     }
>> +     usleep_range(100, 150);
>
> add a comment for the above delay.

sure.

>> +     ret = reset_control_deassert(qphy->phy_reset);
>> +     if (ret) {
>> +             dev_err(&phy->dev, "Failed to de-assert phy_reset\n");
>> +             return ret;
>> +     }
>> +
>> +     /* Disable the PHY */
>> +     qusb2_set_bits(qphy->base + QUSB2PHY_PORT_POWERDOWN,
>> +                     CLAMP_N_EN | FREEZIO_N | POWER_DOWN);
>> +
>> +     /* save reset value to override based on clk scheme */
>> +     reset_val = readl_relaxed(qphy->base + QUSB2PHY_PLL_TEST);
>> +
>> +     qcom_qusb2_phy_configure(qphy->base, qphy->cfg->phy_init_tbl,
>> +                             qphy->cfg->phy_init_tbl_sz);
>> +
>> +     /* Check for efuse value for tuning the PHY */
>> +     ret = qusb2_phy_set_tune2_param(qphy);
>> +     if (ret)
>> +             return ret;
>> +
>> +     /* Enable the PHY */
>> +     qusb2_clr_bits(qphy->base + QUSB2PHY_PORT_POWERDOWN, POWER_DOWN);
>> +
>> +     /* Require to get phy pll lock successfully */
>> +     usleep_range(150, 160);
>> +
>> +     /*
>> +      * Use differential clk by default; later we read TCSR_PHY_CLK_SCHEME
>> +      * register to check if Single-ended clock scheme is selected. If yes,
>> +      * then disable differential ref_clk and use single-ended clock.
>> +      */
>> +
>> +     if (qphy->tcsr) {
>> +             ret = regmap_read(qphy->tcsr, qphy->clk_scheme_offset,
>> +                                                     &clk_scheme);
>> +             /* is SE-clk available ? */
>> +             if (clk_scheme & PHY_CLK_SCHEME_SEL) {
>> +                     dev_dbg(&phy->dev, "%s: select single-ended clk src\n",
>> +                                                             __func__);
>> +                     is_se_clk = true;
>> +             }
>> +     }
>> +
>> +     if (is_se_clk) {
>> +             reset_val |= CLK_REF_SEL;
>> +             clk_disable_unprepare(qphy->ref_clk);
>> +     } else {
>> +             reset_val &= ~CLK_REF_SEL;
>> +             clk_prepare_enable(qphy->ref_clk);
>> +     }
>> +
>> +     writel_relaxed(reset_val, qphy->base + QUSB2PHY_PLL_TEST);
>> +
>> +     /* Make sure that above write is completed to get PLL source clock */
>> +     wmb();
>> +
>> +     /* Required to get PHY PLL lock successfully */
>> +     usleep_range(100, 110);
>> +
>> +     if (!(readb_relaxed(qphy->base + QUSB2PHY_PLL_STATUS) &
>> +                                     PLL_LOCKED)) {
>> +             dev_err(&phy->dev, "QUSB PHY PLL LOCK fails:%x\n",
>> +                     readb_relaxed(qphy->base + QUSB2PHY_PLL_STATUS));
>> +             return -EBUSY;
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int qusb2_phy_exit(struct phy *phy)
>> +{
>> +     struct qusb2_phy *qphy = phy_get_drvdata(phy);
>> +
>> +     /* Disable the PHY */
>> +     qusb2_set_bits(qphy->base + QUSB2PHY_PORT_POWERDOWN,
>> +                     CLAMP_N_EN | FREEZIO_N | POWER_DOWN);
>> +
>> +     qusb2_phy_enable_clocks(qphy, false);
>> +     qusb2_phy_enable_power(qphy, false);
>> +
>> +     return 0;
>> +}
>> +
>> +static const struct phy_ops qusb2_phy_gen_ops = {
>> +     .init           = qusb2_phy_init,
>> +     .exit           = qusb2_phy_exit,
>
> you can add callbacks for power_on and power_off and add then enable clocks and
> power here?

Yes, will do that.

>
>> +     .owner          = THIS_MODULE,
>> +};
>> +
>> +static const struct of_device_id qusb2_phy_of_match_table[] = {
>> +     {
>> +             .compatible     = "qcom,msm8996-qusb2-phy",
>> +             .data           = &msm8996_phy_init_cfg,
>> +     },
>> +     { },
>> +};
>> +MODULE_DEVICE_TABLE(of, qusb2_phy_of_match_table);
>> +
>> +static int qusb2_phy_probe(struct platform_device *pdev)
>> +{
>> +     struct device *dev = &pdev->dev;
>> +     struct qusb2_phy *qphy;
>> +     struct phy_provider *phy_provider;
>> +     struct phy *generic_phy;
>> +     const struct of_device_id *match;
>> +     struct resource *res;
>> +     int ret = 0;
>> +
>> +     qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
>> +     if (!qphy)
>> +             return -ENOMEM;
>> +
>> +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +     if (!res)
>> +             return -ENODEV;
>
> the above check is not required. ioremap will take care.

okay, will remove this.

>> +     qphy->base = devm_ioremap_resource(dev, res);
>> +     if (IS_ERR(qphy->base))
>> +             return PTR_ERR(qphy->base);
>> +
>> +     qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb_clk");
>> +     if (IS_ERR(qphy->cfg_ahb_clk)) {
>> +             ret = PTR_ERR(qphy->cfg_ahb_clk);
>> +             if (ret != -EPROBE_DEFER)
>> +                     dev_err(dev, "failed to get cfg_ahb_clk\n");
>> +             return ret;
>> +     }
>> +
>> +     qphy->ref_clk_src = devm_clk_get(dev, "ref_clk_src");
>> +     if (IS_ERR(qphy->ref_clk_src)) {
>> +             qphy->ref_clk_src = NULL;
>> +             ret = PTR_ERR(qphy->ref_clk_src);
>> +             if (ret != -EPROBE_DEFER)
>> +                     dev_dbg(dev, "clk get failed for ref_clk_src\n");
>
> don't we have to return on PROBE_DEFER?

yes, we do need to return here.

>> +     }
>> +
>> +     qphy->ref_clk = devm_clk_get(dev, "ref_clk");
>> +     if (IS_ERR(qphy->ref_clk)) {
>> +             qphy->ref_clk = NULL;
>> +             ret = PTR_ERR(qphy->ref_clk);
>> +             if (ret != -EPROBE_DEFER)
>> +                     dev_dbg(dev, "clk get failed for ref_clk\n");
>
> same here.

right, will return on EPROBE_DEFER.

>> +     } else {
>> +             clk_set_rate(qphy->ref_clk, 19200000);
>> +     }
>> +
>> +     qphy->iface_clk = devm_clk_get(dev, "iface_clk");
>> +     if (IS_ERR(qphy->iface_clk)) {
>> +             qphy->iface_clk = NULL;
>> +             ret = PTR_ERR(qphy->iface_clk);
>> +             if (ret != -EPROBE_DEFER)
>> +                     dev_dbg(dev, "clk get failed for iface_clk\n");
>
> here..

yup.

>> +     }
>> +
>> +     qphy->phy_reset = devm_reset_control_get(&pdev->dev, "phy");
>> +     if (IS_ERR(qphy->phy_reset)) {
>> +             dev_err(dev, "failed to get phy core reset\n");
>> +             return PTR_ERR(qphy->phy_reset);
>> +     }
>> +
>> +     qphy->vdd_phy = devm_regulator_get(dev, "vdd-phy");
>> +     if (IS_ERR(qphy->vdd_phy)) {
>> +             dev_err(dev, "unable to get vdd-phy supply\n");
>> +             return PTR_ERR(qphy->vdd_phy);
>> +     }
>> +
>> +     qphy->vdda_pll = devm_regulator_get(dev, "vdda-pll");
>> +     if (IS_ERR(qphy->vdda_pll)) {
>> +             dev_err(dev, "unable to get vdda-pll supply\n");
>> +             return PTR_ERR(qphy->vdda_pll);
>> +     }
>> +
>> +     qphy->vdda_phy_dpdm = devm_regulator_get(dev, "vdda-phy-dpdm");
>> +     if (IS_ERR(qphy->vdda_phy_dpdm)) {
>> +             dev_err(dev, "unable to get vdda-phy-dpdm supply\n");
>> +             return PTR_ERR(qphy->vdda_phy_dpdm);
>> +     }
>> +
>> +     /* Get the specific init parameters of QMP phy */
>> +     match = of_match_node(qusb2_phy_of_match_table, dev->of_node);
>> +     qphy->cfg = match->data;
>> +
>> +     qphy->tcsr = syscon_regmap_lookup_by_phandle(dev->of_node,
>> +                                                     "qcom,tcsr-syscon");
>> +     if (!IS_ERR(qphy->tcsr)) {
>> +             ret = of_property_read_u32(dev->of_node, "qcom,phy-clk-scheme",
>> +                                             &qphy->clk_scheme_offset);
>> +             if (ret) {
>> +                     dev_err(dev, "phy-clk-scheme reg offset is invalid\n");
>> +                     qphy->tcsr = NULL;
>> +                     return ret;
>> +             }
>> +     } else {
>> +             dev_dbg(dev, "Failed to lookup TCSR regmap\n");
>> +             qphy->tcsr = NULL;
>> +     }
>> +
>> +     phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
>> +     if (IS_ERR(phy_provider)) {
>> +             ret = PTR_ERR(phy_provider);
>> +             dev_err(dev, "%s: failed to register phy %d\n", __func__, ret);
>> +             return ret;
>> +     }
>> +
>> +     generic_phy = devm_phy_create(dev, NULL, &qusb2_phy_gen_ops);
>> +     if (IS_ERR(generic_phy)) {
>> +             ret = PTR_ERR(generic_phy);
>> +             dev_err(dev, "%s: failed to create phy %d\n", __func__, ret);
>> +             generic_phy = NULL;
>> +             return ret;
>> +     }
>
> phy should be created before registering the PHY.
>> +     qphy->phy = generic_phy;
>> +
>> +     dev_set_drvdata(dev, qphy);
>> +     phy_set_drvdata(generic_phy, qphy);
>
> this should be done before phy_provider_register.

will move the code as suggested.


Thanks
Vivek

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] phy: qcom-qmp: new qmp phy driver for qcom-chipsets
  2016-11-02  7:26         ` Vivek Gautam
@ 2016-11-02  9:33           ` Srinivas Kandagatla
  0 siblings, 0 replies; 19+ messages in thread
From: Srinivas Kandagatla @ 2016-11-02  9:33 UTC (permalink / raw)
  To: Vivek Gautam
  Cc: kishon, robh+dt, Mark Rutland, devicetree, linux-kernel, linux-arm-msm



On 02/11/16 07:26, Vivek Gautam wrote:
>>> +               phy_desc->tx = base + qphy->cfg->tx_offsets[id];
>>> >> +               phy_desc->rx = base + qphy->cfg->rx_offsets[id];
>>> >> +               phy_desc->pcs = base + qphy->cfg->pcs_offsets[id];
>>> >> +
>>> >> +               /*
>>> >> +                * Get PHY's Pipe clock, if any; USB3 and PCIe are PIPE
>>> >> +                * based phys, so they essentially have pipe clock
>>> >> +                */
>>> >> +               memset(&prop_name, 0, sizeof(prop_name));
>>> >> +               snprintf(prop_name, MAX_PROP_NAME, "pipe%d", id);
>>> >> +               phy_desc->pipe_clk = devm_clk_get(dev, prop_name);
>>> >> +               if (IS_ERR(phy_desc->pipe_clk)) {
>>> >> +                       if (qphy->cfg->type == PHY_TYPE_PCIE ||
>>> >> +                           qphy->cfg->type == PHY_TYPE_USB3) {
>>> >> +                               ret = PTR_ERR(phy_desc->pipe_clk);
>>> >> +                               if (ret != -EPROBE_DEFER)
>>> >> +                                       dev_err(dev,
>>> >> +                                       "failed to get lane%d pipe_clk\n",
>>> >> id);
>>> >> +                               return ret;
>>> >> +                       } else {
>>> >> +                               phy_desc->pipe_clk = NULL;
>>> >> +                       }
>>> >> +               }
>> >
>> >
>> > Do we need to really need phy type for this?
>> > You should proably make a flag in cfg to mark this rather then using phy
>> > type. Hopefully it will also simplify the logic.
> The pipe clock is something that's specific to PCIe and USB3 type
> phys (both are PIPE3 specs based phys). So i think it is okay
> to use the PHY_TYPE_{*} macros here. Looks a bit cleaner rather
> than having a cfg flag. No ?
Yes, it makes sense, I was not sure of full details of the pipe3 specs, 
now that you made it more clear.
Remember to add this in comments.

Thanks,
srini
>
> Using PHY_TYPE_{*} macros will also keep the doors open to
> other PHY types that are added at a later point in time.
>
>> >

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] phy: qcom-qmp: new qmp phy driver for qcom-chipsets
       [not found]     ` <ca4054a8-a813-0f50-3224-1e418eaa7095-l0cyMroinI0@public.gmane.org>
@ 2016-11-10  9:03       ` Vivek Gautam
       [not found]         ` <CAFp+6iGMp9pkt5NVTG9f-xjF5VOmj3Om5ngvMboyjdcR94Bhag-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
  0 siblings, 1 reply; 19+ messages in thread
From: Vivek Gautam @ 2016-11-10  9:03 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: robh+dt, Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA

Hi Kishon,


On Thu, Oct 27, 2016 at 1:41 AM, Kishon Vijay Abraham I <kishon-l0cyMroinI0@public.gmane.org> wrote:
> Hi,
>
> On Wednesday 19 October 2016 04:13 PM, Vivek Gautam wrote:
>> Qualcomm SOCs have QMP phy controller that provides support
>> to a number of controller, viz. PCIe, UFS, and USB.
>> Add a new driver, based on generic phy framework, for this
>> phy controller.
>>
>> USB3-phy changes: Based on phy-msm-ssusb-qmp driver available on
>> msm-4.4 kernel @codeaurora[1].
>> PCIe-phy changes: Based on msm8996-pcie-phy driver posted by
>> Srinivas [2].
>>
>> [1] https://source.codeaurora.org/quic/la/kernel/msm-4.4/log/?h=caf/3.18/msm-3.18
>> [2] https://patchwork.kernel.org/patch/9318947/
>
> use only lkml links here.

Ok.

>>
>> Signed-off-by: Vivek Gautam <vivek.gautam-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
>> Cc: Kishon Vijay Abraham I <kishon-l0cyMroinI0@public.gmane.org>
>> ---
>>  .../devicetree/bindings/phy/qcom-qmp-phy.txt       |   61 ++
>>  drivers/phy/Kconfig                                |    8 +
>>  drivers/phy/Makefile                               |    1 +
>>  drivers/phy/phy-qcom-qmp.c                         | 1154 ++++++++++++++++++++
>>  4 files changed, 1224 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
>>  create mode 100644 drivers/phy/phy-qcom-qmp.c
>>
>> diff --git a/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
>> new file mode 100644
>> index 0000000..90214aa
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
>> @@ -0,0 +1,61 @@
>> +Qualcomm QMP PHY
>> +----------------
>> +
>> +QMP phy controller supports physical layer functionality for a number of
>> +controllers on Qualcomm chipsets, such as, PCIe, UFS, and USB.
>> +
>> +Required properties:
>> + - compatible: compatible list, contains:
>> +            "qcom,msm8996-qmp-pcie-phy" for 14nm PCIe phy on msm8996,
>> +            "qcom,msm8996-qmp-usb3-phy" for 14nm USB3 phy on msm8996.
>> + - reg: offset and length of the PHY register set.
>> + - #phy-cells: must be 1
>> +    - Cell after phy phandle should be the port (lane) number.
>> + - clocks: a list of phandles and clock-specifier pairs,
>> +        one for each entry in clock-names.
>> + - clock-names: must be "cfg_ahb" for phy config clock,
>> +                     "aux" for phy aux clock,
>> +                     "ref_clk" for 19.2 MHz ref clk,
>> +                     "ref_clk_src" for reference clock source,
>> +                     "pipe<port-number>" for pipe clock specific to
>> +                     each port/lane (Optional).
>> + - resets: a list of phandles and reset controller specifier pairs,
>> +        one for each entry in reset-names.
>> + - reset-names: must be "phy" for reset of phy block,
>> +                     "common" for phy common block reset,
>> +                     "cfg" for phy's ahb cfg block reset (Optional).
>> +                     "port<port-number>" for reset specific to
>> +                     each port/lane. (Optional)
>> + - vdda-phy-supply: Phandle to a regulator supply to PHY core block.
>> + - vdda-pll-supply: Phandle to 1.8V regulator supply to PHY refclk pll block.
>> +
>> +Optional properties:
>> + - vddp-ref-clk-supply: Phandle to a regulator supply to any specific refclk
>> +                     pll block.
>> +
>> +Example:
>> +     pcie_phy: pciephy@34000 {
>> +             compatible = "qcom,qmp-14nm-pcie-phy";
>> +             reg = <0x034000 0x3fff>;
>> +             #phy-cells = <1>;
>> +
>> +             clocks = <&gcc GCC_PCIE_PHY_AUX_CLK>,
>> +                     <&gcc GCC_PCIE_PHY_CFG_AHB_CLK>,
>> +                     <&gcc GCC_PCIE_0_PIPE_CLK>,
>> +                     <&gcc GCC_PCIE_1_PIPE_CLK>,
>> +                     <&gcc GCC_PCIE_2_PIPE_CLK>;
>> +             clock-names = "aux", "cfg_ahb",
>> +                             "pipe0", "pipe1", "pipe2";
>> +
>> +             vdda-phy-supply = <&pm8994_l28>;
>> +             vdda-pll-supply = <&pm8994_l12>;
>> +
>> +             resets = <&gcc GCC_PCIE_PHY_BCR>,
>> +                     <&gcc GCC_PCIE_PHY_COM_BCR>,
>> +                     <&gcc GCC_PCIE_PHY_COM_NOCSR_BCR>,
>> +                     <&gcc GCC_PCIE_0_PHY_BCR>,
>> +                     <&gcc GCC_PCIE_1_PHY_BCR>,
>> +                     <&gcc GCC_PCIE_2_PHY_BCR>;
>> +             reset-names = "phy", "common", "cfg",
>> +                             "lane0", "lane1", "lane2";
>> +     };
>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>> index 5547984..d5e2b50f 100644
>> --- a/drivers/phy/Kconfig
>> +++ b/drivers/phy/Kconfig
>> @@ -446,6 +446,14 @@ config PHY_STIH41X_USB
>>         Enable this to support the USB transceiver that is part of
>>         STMicroelectronics STiH41x SoC series.
>>
>> +config PHY_QCOM_QMP
>> +     tristate "Qualcomm QMP PHY Driver"
>> +     depends on OF && (ARCH_QCOM || COMPILE_TEST)
>> +     select GENERIC_PHY
>> +     help
>> +       Enable this to support the QMP PHY transceiver that is used
>> +       with controllers such as PCIe, UFS, and USB on Qualcomm chips.
>> +
>>  config PHY_QCOM_QUSB2
>>       tristate "Qualcomm QUSB2 PHY Driver"
>>       depends on OF && (ARCH_QCOM || COMPILE_TEST)
>> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
>> index 848489d..fde9fba 100644
>> --- a/drivers/phy/Makefile
>> +++ b/drivers/phy/Makefile
>> @@ -51,6 +51,7 @@ obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)        += phy-spear1340-miphy.o
>>  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_QMP)   += phy-qcom-qmp.o
>>  obj-$(CONFIG_PHY_QCOM_QUSB2)         += phy-qcom-qusb2.o
>>  obj-$(CONFIG_PHY_QCOM_UFS)   += phy-qcom-ufs.o
>>  obj-$(CONFIG_PHY_QCOM_UFS)   += phy-qcom-ufs-qmp-20nm.o
>> diff --git a/drivers/phy/phy-qcom-qmp.c b/drivers/phy/phy-qcom-qmp.c
>> new file mode 100644
>> index 0000000..7e89179
>> --- /dev/null
>> +++ b/drivers/phy/phy-qcom-qmp.c
>> @@ -0,0 +1,1154 @@
>> +/*
>> + * Copyright (c) 2016, The Linux Foundation. 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/delay.h>
>> +#include <linux/err.h>
>> +#include <linux/io.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/phy/phy.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/reset.h>
>> +#include <linux/slab.h>
>> +

[snip]

>> +
>> +static struct qmp_phy_init_tbl usb3phy_serdes_init_tbl[] = {
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x04),
>> +     /* PLL and Loop filter settings */
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0x00),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x15),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x34),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a),
>> +     /* SSC settings */
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0xde),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x07),
>> +};
>> +
>> +static struct qmp_phy_init_tbl usb3phy_tx_init_tbl[] = {
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_TX_RCV_DETECT_LVL_2, 0x12),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06),
>> +};
>> +
>> +static struct qmp_phy_init_tbl usb3phy_rx_init_tbl[] = {
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xbb),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x03),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x18),
>> +     QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x16),
>> +};
>> +
>> +static struct qmp_phy_init_tbl usb3phy_pcs_init_tbl[] = {
>> +     /* FLL settings */
>> +     QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_CNTRL2, 0x03),
>> +     QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_CNTRL1, 0x02),
>> +     QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_CNT_VAL_L, 0x09),
>> +     QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_CNT_VAL_H_TOL, 0x42),
>> +     QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_MAN_CODE, 0x85),
>> +
>> +     /* Lock Det settings */
>> +     QCOM_QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG1, 0xd1),
>> +     QCOM_QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG2, 0x1f),
>> +     QCOM_QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG3, 0x47),
>> +     QCOM_QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG2, 0x08),
>> +};
>
> if you can implement the dt mechanism I mentioned in your other patch, all
> these tables can be got rid of and the code to initialize these can also be
> removed.

These are lot of configurations for PLL, TX, RX and PCS. I think it's
hard to take
out common bindings that can serve purpose of other PHYs as well.


[snip]

>> +unsigned int msm8996_pciephy_tx_offsets[] = { 0x1000, 0x2000, 0x3000 };
>
> you can have a separate reg map for each lane and all these can come from dt.

The idea is to avoid the any child nodes for lanes. So, we have the complete
ioremaped region and these offsets to tx, rx and pcs blocks.

>> +unsigned int msm8996_pciephy_rx_offsets[] = { 0x1200, 0x2200, 0x3200 };
>> +unsigned int msm8996_pciephy_pcs_offsets[] = { 0x1400, 0x2400, 0x3400 };

[snip]

>> +static int qcom_qmp_phy_poweron(struct phy *phy)
>> +{
>> +     struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
>> +     struct qcom_qmp_phy *qphy = phydesc->qphy;
>> +     int ret;
>> +
>> +     dev_info(&phy->dev, "Powering on QMP phy\n");
>> +
>> +     ret = regulator_enable(qphy->vdda_phy);
>> +     if (ret) {
>> +             dev_err(qphy->dev, "%s: vdda-phy enable failed, err=%d\n",
>> +                             __func__, ret);
>> +             return ret;
>> +     }
>> +
>> +     ret = regulator_enable(qphy->vdda_pll);
>> +     if (ret) {
>> +             dev_err(qphy->dev, "%s: vdda-pll enable failed, err=%d\n",
>> +                             __func__, ret);
>> +             goto err_vdda_pll;
>> +     }
>> +
>> +     if (qphy->vddp_ref_clk) {
>> +             ret = regulator_enable(qphy->vddp_ref_clk);
>> +             if (ret) {
>> +                     dev_err(qphy->dev, "%s: vdda-ref-clk enable failed, err=%d\n",
>> +                                     __func__, ret);
>> +                     goto err_vddp_refclk;
>> +             }
>> +     }
>> +
>> +     if (!qphy->clk_enabled) {
>
> lot of my comments on the previous PHY driver is applicable here. For example
> the clk_enabled is not required.

Sure, will remove this and take care of other things similar to
qusb2 phy driver.

>> +             clk_prepare_enable(qphy->ref_clk_src);
>> +             clk_prepare_enable(qphy->ref_clk);
>> +             clk_prepare_enable(phydesc->pipe_clk);
>> +             qphy->clk_enabled = true;
>> +     }

[snip]

>> +static struct phy *qcom_qmp_phy_xlate(struct device *dev,
>> +                                     struct of_phandle_args *args)
>> +{
>> +     struct qcom_qmp_phy *qphy = dev_get_drvdata(dev);
>> +     int i;
>> +
>> +     if (WARN_ON(args->args[0] >= qphy->cfg->nlanes))
>> +             return ERR_PTR(-ENODEV);
>> +
>> +     for (i = 0; i < qphy->cfg->nlanes; i++) {
>> +             if (qphy->phys[i]->index == args->args[0])
>> +                     break;
>
> finding a PHY based on index is not required. Just have a different label for
> each sub-node and using this label in the controller node should be enough.

Like i said in my comment above, the idea is to avoid any kind of sub-nodes
(child nodes) of the PHY device node. So each lane is registered at
separate indices.


Regards
Vivek

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] phy: qcom-qmp: new qmp phy driver for qcom-chipsets
       [not found]         ` <CAFp+6iGMp9pkt5NVTG9f-xjF5VOmj3Om5ngvMboyjdcR94Bhag-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
@ 2016-11-22  7:47           ` Vivek Gautam
  2016-11-22  9:09             ` Kishon Vijay Abraham I
  0 siblings, 1 reply; 19+ messages in thread
From: Vivek Gautam @ 2016-11-22  7:47 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: robh+dt, Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA

Hi Kishon,


On Thu, Nov 10, 2016 at 2:33 PM, Vivek Gautam
<vivek.gautam-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org> wrote:
> Hi Kishon,
>

>>> +unsigned int msm8996_pciephy_tx_offsets[] = { 0x1000, 0x2000, 0x3000 };
>>> +unsigned int msm8996_pciephy_rx_offsets[] = { 0x1200, 0x2200, 0x3200 };
>>> +unsigned int msm8996_pciephy_pcs_offsets[] = { 0x1400, 0x2400, 0x3400 };

>> you can have a separate reg map for each lane and all these can come from dt.
>
> The idea is to avoid the any child nodes for lanes. So, we have the complete
> ioremaped region and these offsets to tx, rx and pcs blocks.

I don't see benefits in using regmap for different lanes.
Do you see benefits in replacing a bunch of readl()/writel() with
regmap_read()/regmap_update_bits()/regmap_write() ?

I can as well use separate 'reg' values for each lanes, and have the offsets
come from dt. Something like below :

 - reg: array of offset and length of the PHY register sets.
       at index 0: offset and length of register set for PHY common
                   serdes block.
       from index 1 - N: offset and length of register set for each lane,
                         for N number of phy lanes (ports).
 - lane-offsets: array of offsets to tx, rx and pcs blocks for phy lanes.

Example:
       pcie_phy: pciephy@34000 {
               compatible = "qcom,msm8996-qmp-pcie-phy";
               reg = <0x034000 0x48f>,
                       <0x035000 5bf>,
                       <0x036000 5bf>,
                       <0x037000 5bf>;
                                       /* tx, rx, pcs */
               lane-offsets = <0x0 0x200 0x400>;


Does this look OK ? Or should i use regmap only ?


Thanks
Vivek
-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] phy: qcom-qmp: new qmp phy driver for qcom-chipsets
  2016-11-22  7:47           ` Vivek Gautam
@ 2016-11-22  9:09             ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 19+ messages in thread
From: Kishon Vijay Abraham I @ 2016-11-22  9:09 UTC (permalink / raw)
  To: Vivek Gautam
  Cc: robh+dt, Mark Rutland, devicetree, linux-kernel, linux-arm-msm

Hi,

On Tuesday 22 November 2016 01:17 PM, Vivek Gautam wrote:
> Hi Kishon,
> 
> 
> On Thu, Nov 10, 2016 at 2:33 PM, Vivek Gautam
> <vivek.gautam@codeaurora.org> wrote:
>> Hi Kishon,
>>
> 
>>>> +unsigned int msm8996_pciephy_tx_offsets[] = { 0x1000, 0x2000, 0x3000 };
>>>> +unsigned int msm8996_pciephy_rx_offsets[] = { 0x1200, 0x2200, 0x3200 };
>>>> +unsigned int msm8996_pciephy_pcs_offsets[] = { 0x1400, 0x2400, 0x3400 };
> 
>>> you can have a separate reg map for each lane and all these can come from dt.
>>
>> The idea is to avoid the any child nodes for lanes. So, we have the complete
>> ioremaped region and these offsets to tx, rx and pcs blocks.
> 
> I don't see benefits in using regmap for different lanes.
> Do you see benefits in replacing a bunch of readl()/writel() with
> regmap_read()/regmap_update_bits()/regmap_write() ?

By reg map, I meant register spaces allocated for different lanes.
> 
> I can as well use separate 'reg' values for each lanes, and have the offsets
> come from dt. Something like below :
> 
>  - reg: array of offset and length of the PHY register sets.
>        at index 0: offset and length of register set for PHY common
>                    serdes block.
>        from index 1 - N: offset and length of register set for each lane,
>                          for N number of phy lanes (ports).
>  - lane-offsets: array of offsets to tx, rx and pcs blocks for phy lanes.
> 
> Example:
>        pcie_phy: pciephy@34000 {
>                compatible = "qcom,msm8996-qmp-pcie-phy";
>                reg = <0x034000 0x48f>,
>                        <0x035000 5bf>,
>                        <0x036000 5bf>,
>                        <0x037000 5bf>;
>                                        /* tx, rx, pcs */
>                lane-offsets = <0x0 0x200 0x400>;

right, I meant something like this.

Thanks
Kishon

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2016-11-22  9:09 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-10-19 10:43 [PATCH 0/2] phy: USB and PCIe phy drivers for Qcom chipsets Vivek Gautam
2016-10-19 10:43 ` [PATCH 1/2] phy: qcom-qusb2: New driver for QUSB2 PHY on Qcom chips Vivek Gautam
2016-10-20  4:13   ` Vivek Gautam
2016-10-26 19:56   ` Kishon Vijay Abraham I
2016-11-02  8:59     ` Vivek Gautam
2016-10-26 21:16   ` Rob Herring
2016-11-02  8:11     ` Vivek Gautam
2016-10-19 10:43 ` [PATCH 2/2] phy: qcom-qmp: new qmp phy driver for qcom-chipsets Vivek Gautam
2016-10-19 10:54   ` Vivek Gautam
2016-10-20  4:17     ` Vivek Gautam
2016-10-26 20:11   ` Kishon Vijay Abraham I
     [not found]     ` <ca4054a8-a813-0f50-3224-1e418eaa7095-l0cyMroinI0@public.gmane.org>
2016-11-10  9:03       ` Vivek Gautam
     [not found]         ` <CAFp+6iGMp9pkt5NVTG9f-xjF5VOmj3Om5ngvMboyjdcR94Bhag-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2016-11-22  7:47           ` Vivek Gautam
2016-11-22  9:09             ` Kishon Vijay Abraham I
     [not found]   ` <1476873827-7191-3-git-send-email-vivek.gautam-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2016-10-26 13:57     ` Srinivas Kandagatla
     [not found]       ` <a969a6be-472d-a463-790b-1e2f373d19b2-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
2016-11-02  7:26         ` Vivek Gautam
2016-11-02  9:33           ` Srinivas Kandagatla
2016-10-26 21:18     ` Rob Herring
2016-11-02  7:27       ` Vivek Gautam

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).