diff mbox

[v2,2/5] phy: qcom: Add Qualcomm PCIe PHY

Message ID 1430743338-10441-3-git-send-email-svarbanov@mm-sol.com (mailing list archive)
State New, archived
Headers show

Commit Message

Stanimir Varbanov May 4, 2015, 12:42 p.m. UTC
Add a PCIe PHY driver used by PCIe host controller driver
on Qualcomm SoCs like Snapdragon 805.

Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com>
---
 drivers/phy/Kconfig         |    9 ++
 drivers/phy/Makefile        |    1 +
 drivers/phy/phy-qcom-pcie.c |  291 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 301 insertions(+), 0 deletions(-)
 create mode 100644 drivers/phy/phy-qcom-pcie.c

Comments

Kishon Vijay Abraham I May 4, 2015, 2:35 p.m. UTC | #1
Hi,

On Monday 04 May 2015 06:12 PM, Stanimir Varbanov wrote:
> Add a PCIe PHY driver used by PCIe host controller driver
> on Qualcomm SoCs like Snapdragon 805.
>
> Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com>
> ---
>   drivers/phy/Kconfig         |    9 ++
>   drivers/phy/Makefile        |    1 +
>   drivers/phy/phy-qcom-pcie.c |  291 +++++++++++++++++++++++++++++++++++++++++++

Why do you need a new PHY driver for this? Why not use the existing QCOM PHY 
driver. I can see the registers used here in phy-qcom-ufs-qmp-14nm.h?

Thanks
Kishon
Stanimir Varbanov May 4, 2015, 3:24 p.m. UTC | #2
On 05/04/2015 05:35 PM, Kishon Vijay Abraham I wrote:
> Hi,
> 
> On Monday 04 May 2015 06:12 PM, Stanimir Varbanov wrote:
>> Add a PCIe PHY driver used by PCIe host controller driver
>> on Qualcomm SoCs like Snapdragon 805.
>>
>> Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com>
>> ---
>>   drivers/phy/Kconfig         |    9 ++
>>   drivers/phy/Makefile        |    1 +
>>   drivers/phy/phy-qcom-pcie.c |  291
>> +++++++++++++++++++++++++++++++++++++++++++
> 
> Why do you need a new PHY driver for this? Why not use the existing QCOM
> PHY driver. I can see the registers used here in phy-qcom-ufs-qmp-14nm.h?

I agree that on first glance there are similarities, but I'm not sure
does the PHYs are the same IP blocks. Or at least they are different
revisions which have too many differences. So trying to combine them
will lead to more code than now.

Either way I will try to understand how many the differences are.
Bjorn Helgaas May 19, 2015, 10:41 p.m. UTC | #3
On Mon, May 04, 2015 at 06:24:10PM +0300, Stanimir Varbanov wrote:
> On 05/04/2015 05:35 PM, Kishon Vijay Abraham I wrote:
> > Hi,
> > 
> > On Monday 04 May 2015 06:12 PM, Stanimir Varbanov wrote:
> >> Add a PCIe PHY driver used by PCIe host controller driver
> >> on Qualcomm SoCs like Snapdragon 805.
> >>
> >> Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com>
> >> ---
> >>   drivers/phy/Kconfig         |    9 ++
> >>   drivers/phy/Makefile        |    1 +
> >>   drivers/phy/phy-qcom-pcie.c |  291
> >> +++++++++++++++++++++++++++++++++++++++++++
> > 
> > Why do you need a new PHY driver for this? Why not use the existing QCOM
> > PHY driver. I can see the registers used here in phy-qcom-ufs-qmp-14nm.h?
> 
> I agree that on first glance there are similarities, but I'm not sure
> does the PHYs are the same IP blocks. Or at least they are different
> revisions which have too many differences. So trying to combine them
> will lead to more code than now.
> 
> Either way I will try to understand how many the differences are.

Ping, where are we with this?  Should I wait for something else, or are you
convinced there's enough difference to warrant a new PHY driver, Kishon?

Bjorn
Kishon Vijay Abraham I May 20, 2015, 1:08 p.m. UTC | #4
Hi Bjorn,

On Wednesday 20 May 2015 04:11 AM, Bjorn Helgaas wrote:
> On Mon, May 04, 2015 at 06:24:10PM +0300, Stanimir Varbanov wrote:
>> On 05/04/2015 05:35 PM, Kishon Vijay Abraham I wrote:
>>> Hi,
>>>
>>> On Monday 04 May 2015 06:12 PM, Stanimir Varbanov wrote:
>>>> Add a PCIe PHY driver used by PCIe host controller driver
>>>> on Qualcomm SoCs like Snapdragon 805.
>>>>
>>>> Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com>
>>>> ---
>>>>    drivers/phy/Kconfig         |    9 ++
>>>>    drivers/phy/Makefile        |    1 +
>>>>    drivers/phy/phy-qcom-pcie.c |  291
>>>> +++++++++++++++++++++++++++++++++++++++++++
>>>
>>> Why do you need a new PHY driver for this? Why not use the existing QCOM
>>> PHY driver. I can see the registers used here in phy-qcom-ufs-qmp-14nm.h?
>>
>> I agree that on first glance there are similarities, but I'm not sure
>> does the PHYs are the same IP blocks. Or at least they are different
>> revisions which have too many differences. So trying to combine them
>> will lead to more code than now.
>>
>> Either way I will try to understand how many the differences are.
>
> Ping, where are we with this?  Should I wait for something else, or are you
> convinced there's enough difference to warrant a new PHY driver, Kishon?

I'd like to wait to see if Stanimir can use existing driver instead of creating 
a new driver.

Cheers
Kishon
Bjorn Helgaas May 20, 2015, 1:23 p.m. UTC | #5
On Wed, May 20, 2015 at 8:08 AM, Kishon Vijay Abraham I <kishon@ti.com> wrote:
> Hi Bjorn,
>
>
> On Wednesday 20 May 2015 04:11 AM, Bjorn Helgaas wrote:
>>
>> On Mon, May 04, 2015 at 06:24:10PM +0300, Stanimir Varbanov wrote:
>>>
>>> On 05/04/2015 05:35 PM, Kishon Vijay Abraham I wrote:
>>>>
>>>> Hi,
>>>>
>>>> On Monday 04 May 2015 06:12 PM, Stanimir Varbanov wrote:
>>>>>
>>>>> Add a PCIe PHY driver used by PCIe host controller driver
>>>>> on Qualcomm SoCs like Snapdragon 805.
>>>>>
>>>>> Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com>
>>>>> ---
>>>>>    drivers/phy/Kconfig         |    9 ++
>>>>>    drivers/phy/Makefile        |    1 +
>>>>>    drivers/phy/phy-qcom-pcie.c |  291
>>>>> +++++++++++++++++++++++++++++++++++++++++++
>>>>
>>>>
>>>> Why do you need a new PHY driver for this? Why not use the existing QCOM
>>>> PHY driver. I can see the registers used here in
>>>> phy-qcom-ufs-qmp-14nm.h?
>>>
>>>
>>> I agree that on first glance there are similarities, but I'm not sure
>>> does the PHYs are the same IP blocks. Or at least they are different
>>> revisions which have too many differences. So trying to combine them
>>> will lead to more code than now.
>>>
>>> Either way I will try to understand how many the differences are.
>>
>>
>> Ping, where are we with this?  Should I wait for something else, or are
>> you
>> convinced there's enough difference to warrant a new PHY driver, Kishon?
>
>
> I'd like to wait to see if Stanimir can use existing driver instead of
> creating a new driver.

OK, I'm waiting for a v3 with either tweaks to the QCOM PHY driver, or
stronger justification as to why that's impossible.  It's hard to
quantify statements like "there are similarities, but there are too
many differences," so you might have to actually attempt a patch for
the QCOM code and we can see how ugly that would turn out to be.

Thanks, Kishon.

Bjorn
Stanimir Varbanov May 22, 2015, 4:25 p.m. UTC | #6
On 05/20/2015 04:23 PM, Bjorn Helgaas wrote:
> On Wed, May 20, 2015 at 8:08 AM, Kishon Vijay Abraham I <kishon@ti.com> wrote:
>> Hi Bjorn,
>>
>>
>> On Wednesday 20 May 2015 04:11 AM, Bjorn Helgaas wrote:
>>>
>>> On Mon, May 04, 2015 at 06:24:10PM +0300, Stanimir Varbanov wrote:
>>>>
>>>> On 05/04/2015 05:35 PM, Kishon Vijay Abraham I wrote:
>>>>>
>>>>> Hi,
>>>>>
>>>>> On Monday 04 May 2015 06:12 PM, Stanimir Varbanov wrote:
>>>>>>
>>>>>> Add a PCIe PHY driver used by PCIe host controller driver
>>>>>> on Qualcomm SoCs like Snapdragon 805.
>>>>>>
>>>>>> Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com>
>>>>>> ---
>>>>>>    drivers/phy/Kconfig         |    9 ++
>>>>>>    drivers/phy/Makefile        |    1 +
>>>>>>    drivers/phy/phy-qcom-pcie.c |  291
>>>>>> +++++++++++++++++++++++++++++++++++++++++++
>>>>>
>>>>>
>>>>> Why do you need a new PHY driver for this? Why not use the existing QCOM
>>>>> PHY driver. I can see the registers used here in
>>>>> phy-qcom-ufs-qmp-14nm.h?
>>>>
>>>>
>>>> I agree that on first glance there are similarities, but I'm not sure
>>>> does the PHYs are the same IP blocks. Or at least they are different
>>>> revisions which have too many differences. So trying to combine them
>>>> will lead to more code than now.
>>>>
>>>> Either way I will try to understand how many the differences are.
>>>
>>>
>>> Ping, where are we with this?  Should I wait for something else, or are
>>> you
>>> convinced there's enough difference to warrant a new PHY driver, Kishon?
>>
>>
>> I'd like to wait to see if Stanimir can use existing driver instead of
>> creating a new driver.
> 
> OK, I'm waiting for a v3 with either tweaks to the QCOM PHY driver, or
> stronger justification as to why that's impossible.  It's hard to
> quantify statements like "there are similarities, but there are too
> many differences," so you might have to actually attempt a patch for
> the QCOM code and we can see how ugly that would turn out to be.

Kishon,

I'm still checking the differences, sorry for the delay.

Bjorn, is the pcie driver looks good enough to be merged?
Bjorn Helgaas May 22, 2015, 6:06 p.m. UTC | #7
[+cc Jingoo, Pratyush]

On Fri, May 22, 2015 at 11:25 AM, Stanimir Varbanov
<svarbanov@mm-sol.com> wrote:
> On 05/20/2015 04:23 PM, Bjorn Helgaas wrote:
>> On Wed, May 20, 2015 at 8:08 AM, Kishon Vijay Abraham I <kishon@ti.com> wrote:
>>> Hi Bjorn,
>>>
>>>
>>> On Wednesday 20 May 2015 04:11 AM, Bjorn Helgaas wrote:
>>>>
>>>> On Mon, May 04, 2015 at 06:24:10PM +0300, Stanimir Varbanov wrote:
>>>>>
>>>>> On 05/04/2015 05:35 PM, Kishon Vijay Abraham I wrote:
>>>>>>
>>>>>> Hi,
>>>>>>
>>>>>> On Monday 04 May 2015 06:12 PM, Stanimir Varbanov wrote:
>>>>>>>
>>>>>>> Add a PCIe PHY driver used by PCIe host controller driver
>>>>>>> on Qualcomm SoCs like Snapdragon 805.
>>>>>>>
>>>>>>> Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com>
>>>>>>> ---
>>>>>>>    drivers/phy/Kconfig         |    9 ++
>>>>>>>    drivers/phy/Makefile        |    1 +
>>>>>>>    drivers/phy/phy-qcom-pcie.c |  291
>>>>>>> +++++++++++++++++++++++++++++++++++++++++++
>>>>>>
>>>>>>
>>>>>> Why do you need a new PHY driver for this? Why not use the existing QCOM
>>>>>> PHY driver. I can see the registers used here in
>>>>>> phy-qcom-ufs-qmp-14nm.h?
>>>>>
>>>>>
>>>>> I agree that on first glance there are similarities, but I'm not sure
>>>>> does the PHYs are the same IP blocks. Or at least they are different
>>>>> revisions which have too many differences. So trying to combine them
>>>>> will lead to more code than now.
>>>>>
>>>>> Either way I will try to understand how many the differences are.
>>>>
>>>>
>>>> Ping, where are we with this?  Should I wait for something else, or are
>>>> you
>>>> convinced there's enough difference to warrant a new PHY driver, Kishon?
>>>
>>>
>>> I'd like to wait to see if Stanimir can use existing driver instead of
>>> creating a new driver.
>>
>> OK, I'm waiting for a v3 with either tweaks to the QCOM PHY driver, or
>> stronger justification as to why that's impossible.  It's hard to
>> quantify statements like "there are similarities, but there are too
>> many differences," so you might have to actually attempt a patch for
>> the QCOM code and we can see how ugly that would turn out to be.
>
> Kishon,
>
> I'm still checking the differences, sorry for the delay.
>
> Bjorn, is the pcie driver looks good enough to be merged?

I haven't looked at that (I was waiting until I could deal with the
whole series).  But it sounds like it would be useful to merge patches
3-5 by themselves?  I'll take a look at those.

It would be good to get Arnd and Rob to take a look, especially at the DT parts.

And Jingoo and Pratyush since you use the DesignWare core.

Bjorn
diff mbox

Patch

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index a53bd5b..6cf9065 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -257,6 +257,15 @@  config PHY_QCOM_IPQ806X_SATA
 	depends on OF
 	select GENERIC_PHY
 
+config PHY_QCOM_PCIE
+	tristate "Qualcomm PCIe SerDes/PHY driver"
+	depends on ARCH_QCOM
+	depends on HAS_IOMEM
+	depends on OF
+	select GENERIC_PHY
+	help
+	  Enable support for PCIe phy driver
+
 config PHY_ROCKCHIP_USB
 	tristate "Rockchip USB2 PHY Driver"
 	depends on ARCH_ROCKCHIP && OF
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index f126251..d80b99b 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -32,6 +32,7 @@  obj-$(CONFIG_PHY_EXYNOS5_USBDRD)	+= phy-exynos5-usbdrd.o
 obj-$(CONFIG_PHY_QCOM_APQ8064_SATA)	+= phy-qcom-apq8064-sata.o
 obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
 obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA)	+= phy-qcom-ipq806x-sata.o
+obj-$(CONFIG_PHY_QCOM_PCIE)		+= phy-qcom-pcie.o
 obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY)	+= phy-spear1310-miphy.o
 obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+= phy-spear1340-miphy.o
 obj-$(CONFIG_PHY_XGENE)			+= phy-xgene.o
diff --git a/drivers/phy/phy-qcom-pcie.c b/drivers/phy/phy-qcom-pcie.c
new file mode 100644
index 0000000..0ce2fc6
--- /dev/null
+++ b/drivers/phy/phy-qcom-pcie.c
@@ -0,0 +1,291 @@ 
+/*
+ * Copyright (c) 2014, 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/device.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#define QSERDES_COM_PLL_CP_SETI			0x024
+#define QSERDES_COM_PLL_IP_SETP			0x028
+#define QSERDES_COM_PLL_CP_SETP			0x02c
+#define QSERDES_COM_SYSCLK_EN_SEL		0x038
+#define QSERDES_COM_RESETSM_CNTRL		0x040
+#define QSERDES_COM_PLLLOCK_CMP1		0x044
+#define QSERDES_COM_PLLLOCK_CMP2		0x048
+#define QSERDES_COM_PLLLOCK_CMP_EN		0x050
+#define QSERDES_COM_DEC_START1			0x064
+#define QSERDES_COM_DIV_FRAC_START1		0x098
+#define QSERDES_COM_DIV_FRAC_START2		0x09c
+#define QSERDES_COM_DIV_FRAC_START3		0x0a0
+#define QSERDES_COM_DEC_START2			0x0a4
+#define QSERDES_COM_PLL_RXTXEPCLK_EN		0x0a8
+#define QSERDES_COM_PLL_CRCTRL			0x0ac
+
+#define QSERDES_RX_CDR_CONTROL			0x400
+#define QSERDES_RX_CDR_CONTROL2			0x410
+#define QSERDES_RX_RX_TERM_HIGHZ_CM_AC_COUPLE	0x42c
+#define QSERDES_RX_RX_EQ_GAIN12			0x430
+
+#define PCIE_PHY_SW_RESET			0x600
+#define PCIE_PHY_POWER_DOWN_CONTROL		0x604
+#define PCIE_PHY_START				0x608
+#define PCIE_PHY_ENDPOINT_REFCLK_DRIVE		0x648
+#define PCIE_PHY_POWER_STATE_CONFIG1		0x650
+#define PCIE_PHY_POWER_STATE_CONFIG2		0x654
+#define PCIE_PHY_PWRUP_RESET_DLY_TIME_SYSCLK	0x678
+#define PCIE_PHY_PWRUP_RESET_DLY_TIME_AUXCLK	0x67c
+#define PCIE_PHY_PCS_STATUS			0x6c8
+
+#define PHY_DELAY_MIN_US			995
+#define PHY_DELAY_MAX_US			1005
+#define PHY_RETRIES_COUNT			10
+
+#define PIPE_CLK_DELAY_MIN_US			5000
+#define PIPE_CLK_DELAY_MAX_US			5100
+#define PIPE_CLK_RETRIES_COUNT			10
+
+struct qcom_pcie_phy {
+	void __iomem *base;
+	struct clk *clk;
+	struct reset_control *res_phy;
+	struct regulator *vdda_pll;
+	struct device *dev;
+};
+
+struct phy_regs {
+	u32 reg_offset;
+	u32 val;
+};
+
+static const struct phy_regs pcie_phy_pwrup[] = {
+	{ PCIE_PHY_POWER_DOWN_CONTROL,			0x03 },
+	{ QSERDES_COM_SYSCLK_EN_SEL,			0x08 },
+	{ QSERDES_COM_DEC_START1,			0x82 },
+	{ QSERDES_COM_DEC_START2,			0x03 },
+	{ QSERDES_COM_DIV_FRAC_START1,			0xd5 },
+	{ QSERDES_COM_DIV_FRAC_START2,			0xaa },
+	{ QSERDES_COM_DIV_FRAC_START3,			0x13 },
+	{ QSERDES_COM_PLLLOCK_CMP_EN,			0x01 },
+	{ QSERDES_COM_PLLLOCK_CMP1,			0x2b },
+	{ QSERDES_COM_PLLLOCK_CMP2,			0x68 },
+	{ QSERDES_COM_PLL_CRCTRL,			0xff },
+	{ QSERDES_COM_PLL_CP_SETI,			0x3f },
+	{ QSERDES_COM_PLL_IP_SETP,			0x07 },
+	{ QSERDES_COM_PLL_CP_SETP,			0x03 },
+	{ QSERDES_RX_CDR_CONTROL,			0xf3 },
+	{ QSERDES_RX_CDR_CONTROL2,			0x6b },
+	{ QSERDES_COM_RESETSM_CNTRL,			0x10 },
+	{ QSERDES_RX_RX_TERM_HIGHZ_CM_AC_COUPLE,	0x87 },
+	{ QSERDES_RX_RX_EQ_GAIN12,			0x54 },
+	{ PCIE_PHY_POWER_STATE_CONFIG1,			0xa3 },
+	{ PCIE_PHY_POWER_STATE_CONFIG2,			0xcb },
+	{ QSERDES_COM_PLL_RXTXEPCLK_EN,			0x10 },
+	{ PCIE_PHY_ENDPOINT_REFCLK_DRIVE,		0x10 },
+	{ PCIE_PHY_SW_RESET,				0x00 },
+	{ PCIE_PHY_START,				0x03 },
+};
+
+static const struct phy_regs pcie_phy_pwrdown[] = {
+	{ PCIE_PHY_SW_RESET,				0x01 },
+	{ PCIE_PHY_POWER_DOWN_CONTROL,			0x00 },
+};
+
+static void qcom_pcie_phy_pwr(struct qcom_pcie_phy *pcie,
+			      const struct phy_regs *regs, int arr_size)
+{
+	int i;
+
+	for (i = 0; i < arr_size; i++)
+		writel(regs[i].val, pcie->base + regs[i].reg_offset);
+}
+
+static void qcom_pcie_phy_pwrup(struct qcom_pcie_phy *pcie)
+{
+	qcom_pcie_phy_pwr(pcie, pcie_phy_pwrup, ARRAY_SIZE(pcie_phy_pwrup));
+}
+
+static void qcom_pcie_phy_pwrdown(struct qcom_pcie_phy *pcie)
+{
+	qcom_pcie_phy_pwr(pcie, pcie_phy_pwrdown, ARRAY_SIZE(pcie_phy_pwrdown));
+}
+
+static bool qcom_pcie_phy_is_ready(struct qcom_pcie_phy *pcie)
+{
+	u32 val = readl(pcie->base + PCIE_PHY_PCS_STATUS);
+
+	return val & BIT(6) ? false : true;
+}
+
+static int qcom_pcie_phy_power_on(struct phy *phy)
+{
+	struct qcom_pcie_phy *pcie = phy_get_drvdata(phy);
+	struct device *dev = pcie->dev;
+	int ret, retries;
+
+	ret = regulator_enable(pcie->vdda_pll);
+	if (ret) {
+		dev_err(dev, "cannot enable vdda_pll regulator\n");
+		return ret;
+	}
+
+	ret = reset_control_deassert(pcie->res_phy);
+	if (ret) {
+		dev_err(dev, "cannot deassert phy reset\n");
+		goto fail_vdda_pll;
+	}
+
+	qcom_pcie_phy_pwrup(pcie);
+
+	ret = clk_set_rate(pcie->clk, ~0);
+	if (ret) {
+		dev_err(dev, "cannot set phy clock rate\n");
+		goto fail_res;
+	}
+
+	/*
+	 * setting pipe rate takes time, try arbitrary delay before enabling
+	 * the clock
+	 */
+	retries = PIPE_CLK_RETRIES_COUNT;
+	do {
+		usleep_range(PIPE_CLK_DELAY_MIN_US, PIPE_CLK_DELAY_MAX_US);
+
+		ret = clk_prepare_enable(pcie->clk);
+		if (!ret)
+			break;
+	} while (retries--);
+
+	if (retries < 0) {
+		dev_err(dev, "cannot enable phy clock\n");
+		goto fail_res;
+	}
+
+	retries = PHY_RETRIES_COUNT;
+	do {
+		ret = qcom_pcie_phy_is_ready(pcie);
+		if (ret)
+			break;
+		usleep_range(PHY_DELAY_MIN_US, PHY_DELAY_MAX_US);
+	} while (retries--);
+
+	if (retries < 0) {
+		dev_err(dev, "cannot power on phy\n");
+		ret = -ETIMEDOUT;
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	clk_disable_unprepare(pcie->clk);
+fail_res:
+	reset_control_assert(pcie->res_phy);
+fail_vdda_pll:
+	regulator_disable(pcie->vdda_pll);
+
+	return ret;
+}
+
+static int qcom_pcie_phy_power_off(struct phy *phy)
+{
+	struct qcom_pcie_phy *pcie = phy_get_drvdata(phy);
+
+	clk_disable_unprepare(pcie->clk);
+	qcom_pcie_phy_pwrdown(pcie);
+	reset_control_assert(pcie->res_phy);
+	regulator_disable(pcie->vdda_pll);
+
+	return 0;
+}
+
+static const struct phy_ops qcom_pcie_phy_ops = {
+	.power_on = qcom_pcie_phy_power_on,
+	.power_off = qcom_pcie_phy_power_off,
+	.owner = THIS_MODULE,
+};
+
+static int qcom_pcie_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct phy_provider *provider;
+	struct qcom_pcie_phy *pcie;
+	struct resource *res;
+	struct phy *phy;
+
+	pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
+	if (!pcie)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	pcie->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(pcie->base))
+		return PTR_ERR(pcie->base);
+
+	pcie->clk = devm_clk_get(dev, "core");
+	if (IS_ERR(pcie->clk))
+		return PTR_ERR(pcie->clk);
+
+	pcie->vdda_pll = devm_regulator_get(dev, "vdda_pll");
+	if (IS_ERR(pcie->vdda_pll))
+		return PTR_ERR(pcie->vdda_pll);
+
+	pcie->res_phy = devm_reset_control_get(dev, "core");
+	if (IS_ERR(pcie->res_phy))
+		return PTR_ERR(pcie->res_phy);
+
+	phy = devm_phy_create(dev, NULL, &qcom_pcie_phy_ops);
+	if (IS_ERR(phy)) {
+		dev_err(dev, "cannot create phy\n");
+		return PTR_ERR(phy);
+	}
+
+	provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+	if (IS_ERR(provider)) {
+		dev_err(dev, "cannot register phy provider\n");
+		return PTR_ERR(provider);
+	}
+
+	pcie->dev = dev;
+	phy_set_drvdata(phy, pcie);
+	platform_set_drvdata(pdev, pcie);
+
+	return 0;
+}
+
+static const struct of_device_id qcom_pcie_phy_of_match[] = {
+	{ .compatible = "qcom,pcie-phy" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, qcom_pcie_phy_of_match);
+
+static struct platform_driver qcom_pcie_phy_driver = {
+	.probe = qcom_pcie_phy_probe,
+	.driver = {
+		.name = "pcie-phy",
+		.of_match_table = qcom_pcie_phy_of_match,
+	}
+};
+
+module_platform_driver(qcom_pcie_phy_driver);
+
+MODULE_AUTHOR("Stanimir Varbanov <svarbanov@mm-sol.com>");
+MODULE_DESCRIPTION("Qualcomm PCIe PHY driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:pcie-phy");