diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 493e9354dd..955351c5c2 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -43,4 +43,4 @@ obj-$(CONFIG_PCI_PHYTIUM) += pcie_phytium.o
 obj-$(CONFIG_PCIE_INTEL_FPGA) += pcie_intel_fpga.o
 obj-$(CONFIG_PCI_KEYSTONE) += pcie_dw_ti.o
 obj-$(CONFIG_PCIE_MEDIATEK) += pcie_mediatek.o
-obj-$(CONFIG_PCIE_ROCKCHIP) += pcie_rockchip.o
+obj-$(CONFIG_PCIE_ROCKCHIP) += pcie_rockchip.o pcie_rockchip_phy.o
diff --git a/drivers/pci/pcie_rockchip.c b/drivers/pci/pcie_rockchip.c
index 3f06f783ca..82a8396e42 100644
--- a/drivers/pci/pcie_rockchip.c
+++ b/drivers/pci/pcie_rockchip.c
@@ -159,6 +159,8 @@ static int rockchip_pcie_atr_init(struct rockchip_pcie *priv)
 static int rockchip_pcie_init_port(struct udevice *dev)
 {
 	struct rockchip_pcie *priv = dev_get_priv(dev);
+	struct rockchip_pcie_phy *phy = pcie_get_phy(priv);
+	struct rockchip_pcie_phy_ops *ops = phy_get_ops(phy);
 	u32 cr, val, status;
 	int ret;
 
@@ -183,29 +185,35 @@ static int rockchip_pcie_init_port(struct udevice *dev)
 		return ret;
 	}
 
+	ret = ops->init(phy);
+	if (ret) {
+		dev_err(dev, "failed to init phy (ret=%d)\n", ret);
+		goto err_exit_phy;
+	}
+
 	ret = reset_assert(&priv->core_rst);
 	if (ret) {
 		dev_err(dev, "failed to assert core reset (ret=%d)\n", ret);
-		return ret;
+		goto err_exit_phy;
 	}
 
 	ret = reset_assert(&priv->mgmt_rst);
 	if (ret) {
 		dev_err(dev, "failed to assert mgmt reset (ret=%d)\n", ret);
-		return ret;
+		goto err_exit_phy;
 	}
 
 	ret = reset_assert(&priv->mgmt_sticky_rst);
 	if (ret) {
 		dev_err(dev, "failed to assert mgmt-sticky reset (ret=%d)\n",
 			ret);
-		return ret;
+		goto err_exit_phy;
 	}
 
 	ret = reset_assert(&priv->pipe_rst);
 	if (ret) {
 		dev_err(dev, "failed to assert pipe reset (ret=%d)\n", ret);
-		return ret;
+		goto err_exit_phy;
 	}
 
 	udelay(10);
@@ -213,19 +221,19 @@ static int rockchip_pcie_init_port(struct udevice *dev)
 	ret = reset_deassert(&priv->pm_rst);
 	if (ret) {
 		dev_err(dev, "failed to deassert pm reset (ret=%d)\n", ret);
-		return ret;
+		goto err_exit_phy;
 	}
 
 	ret = reset_deassert(&priv->aclk_rst);
 	if (ret) {
 		dev_err(dev, "failed to deassert aclk reset (ret=%d)\n", ret);
-		return ret;
+		goto err_exit_phy;
 	}
 
 	ret = reset_deassert(&priv->pclk_rst);
 	if (ret) {
 		dev_err(dev, "failed to deassert pclk reset (ret=%d)\n", ret);
-		return ret;
+		goto err_exit_phy;
 	}
 
 	/* Select GEN1 for now */
@@ -234,29 +242,35 @@ static int rockchip_pcie_init_port(struct udevice *dev)
 	cr |= PCIE_CLIENT_CONF_ENABLE | PCIE_CLIENT_MODE_RC;
 	writel(cr, priv->apb_base + PCIE_CLIENT_CONFIG);
 
+	ret = ops->power_on(phy);
+	if (ret) {
+		dev_err(dev, "failed to power on phy (ret=%d)\n", ret);
+		goto err_power_off_phy;
+	}
+
 	ret = reset_deassert(&priv->mgmt_sticky_rst);
 	if (ret) {
 		dev_err(dev, "failed to deassert mgmt-sticky reset (ret=%d)\n",
 			ret);
-		return ret;
+		goto err_power_off_phy;
 	}
 
 	ret = reset_deassert(&priv->core_rst);
 	if (ret) {
 		dev_err(dev, "failed to deassert core reset (ret=%d)\n", ret);
-		return ret;
+		goto err_power_off_phy;
 	}
 
 	ret = reset_deassert(&priv->mgmt_rst);
 	if (ret) {
 		dev_err(dev, "failed to deassert mgmt reset (ret=%d)\n", ret);
-		return ret;
+		goto err_power_off_phy;
 	}
 
 	ret = reset_deassert(&priv->pipe_rst);
 	if (ret) {
 		dev_err(dev, "failed to deassert pipe reset (ret=%d)\n", ret);
-		return ret;
+		goto err_power_off_phy;
 	}
 
 	/* Enable Gen1 training */
@@ -271,7 +285,7 @@ static int rockchip_pcie_init_port(struct udevice *dev)
 			status, PCIE_LINK_UP(status), 20, 500 * 1000);
 	if (ret) {
 		dev_err(dev, "PCIe link training gen1 timeout!\n");
-		return ret;
+		goto err_power_off_phy;
 	}
 
 	/* Initialize Root Complex registers. */
@@ -291,10 +305,16 @@ static int rockchip_pcie_init_port(struct udevice *dev)
 	ret = rockchip_pcie_atr_init(priv);
 	if (ret) {
 		dev_err(dev, "PCIE-%d: ATR init failed\n", dev->seq);
-		return ret;
+		goto err_power_off_phy;
 	}
 
 	return 0;
+
+err_power_off_phy:
+	ops->power_off(phy);
+err_exit_phy:
+	ops->exit(phy);
+	return ret;
 }
 
 static int rockchip_pcie_set_vpcie(struct udevice *dev)
@@ -433,6 +453,10 @@ static int rockchip_pcie_probe(struct udevice *dev)
 	if (ret)
 		return ret;
 
+	ret = rockchip_pcie_phy_get(dev);
+	if (ret)
+		return ret;
+
 	ret = rockchip_pcie_set_vpcie(dev);
 	if (ret)
 		return ret;
diff --git a/drivers/pci/pcie_rockchip.h b/drivers/pci/pcie_rockchip.h
index 6ded5c9553..c3a0a2846d 100644
--- a/drivers/pci/pcie_rockchip.h
+++ b/drivers/pci/pcie_rockchip.h
@@ -53,11 +53,61 @@
 #define PCIE_ATR_OB_REGION0_SIZE	(32 * 1024 * 1024)
 #define PCIE_ATR_OB_REGION_SIZE		(1 * 1024 * 1024)
 
+/*
+ * The higher 16-bit of this register is used for write protection
+ * only if BIT(x + 16) set to 1 the BIT(x) can be written.
+ */
+#define HIWORD_UPDATE_MASK(val, mask, shift) \
+		((val) << (shift) | (mask) << ((shift) + 16))
+
+#define PHY_CFG_DATA_SHIFT    7
+#define PHY_CFG_ADDR_SHIFT    1
+#define PHY_CFG_DATA_MASK     0xf
+#define PHY_CFG_ADDR_MASK     0x3f
+#define PHY_CFG_RD_MASK       0x3ff
+#define PHY_CFG_WR_ENABLE     1
+#define PHY_CFG_WR_DISABLE    1
+#define PHY_CFG_WR_SHIFT      0
+#define PHY_CFG_WR_MASK       1
+#define PHY_CFG_PLL_LOCK      0x10
+#define PHY_CFG_CLK_TEST      0x10
+#define PHY_CFG_CLK_SCC       0x12
+#define PHY_CFG_SEPE_RATE     BIT(3)
+#define PHY_CFG_PLL_100M      BIT(3)
+#define PHY_PLL_LOCKED        BIT(9)
+#define PHY_PLL_OUTPUT        BIT(10)
+#define PHY_LANE_IDLE_OFF     0x1
+#define PHY_LANE_IDLE_MASK    0x1
+#define PHY_LANE_IDLE_A_SHIFT 3
+#define PHY_LANE_IDLE_B_SHIFT 4
+#define PHY_LANE_IDLE_C_SHIFT 5
+#define PHY_LANE_IDLE_D_SHIFT 6
+
+#define PCIE_PHY_CONF		0xe220
+#define PCIE_PHY_STATUS		0xe2a4
+#define PCIE_PHY_LANEOFF	0xe214
+
+struct rockchip_pcie_phy {
+	void *reg_base;
+	struct clk refclk;
+	struct reset_ctl phy_rst;
+	struct rockchip_pcie_phy_ops *ops;
+};
+
+struct rockchip_pcie_phy_ops {
+	int (*init)(struct rockchip_pcie_phy *phy);
+	int (*exit)(struct rockchip_pcie_phy *phy);
+	int (*power_on)(struct rockchip_pcie_phy *phy);
+	int (*power_off)(struct rockchip_pcie_phy *phy);
+};
+
 struct rockchip_pcie {
 	fdt_addr_t axi_base;
 	fdt_addr_t apb_base;
 	int first_busno;
 	struct udevice *dev;
+	struct rockchip_pcie_phy rk_phy;
+	struct rockchip_pcie_phy *phy;
 
 	/* resets */
 	struct reset_ctl core_rst;
@@ -77,3 +127,16 @@ struct rockchip_pcie {
 	struct udevice *vpcie1v8;
 	struct udevice *vpcie0v9;
 };
+
+int rockchip_pcie_phy_get(struct udevice *dev);
+
+inline struct rockchip_pcie_phy *pcie_get_phy(struct rockchip_pcie *pcie)
+{
+	return pcie->phy;
+}
+
+inline
+struct rockchip_pcie_phy_ops *phy_get_ops(struct rockchip_pcie_phy *phy)
+{
+	return (struct rockchip_pcie_phy_ops *)phy->ops;
+}
diff --git a/drivers/pci/pcie_rockchip_phy.c b/drivers/pci/pcie_rockchip_phy.c
new file mode 100644
index 0000000000..47f5d6c7e3
--- /dev/null
+++ b/drivers/pci/pcie_rockchip_phy.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Rockchip PCIe PHY driver
+ *
+ * Copyright (c) 2016 Rockchip, Inc.
+ * Copyright (c) 2020 Amarula Solutions(India)
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <reset.h>
+#include <syscon.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <linux/iopoll.h>
+#include <asm/arch-rockchip/clock.h>
+
+#include "pcie_rockchip.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static void phy_wr_cfg(struct rockchip_pcie_phy *phy, u32 addr, u32 data)
+{
+	u32 reg;
+
+	reg = HIWORD_UPDATE_MASK(data, PHY_CFG_DATA_MASK, PHY_CFG_DATA_SHIFT);
+	reg |= HIWORD_UPDATE_MASK(addr, PHY_CFG_ADDR_MASK, PHY_CFG_ADDR_SHIFT);
+	writel(reg, phy->reg_base + PCIE_PHY_CONF);
+
+	udelay(1);
+
+	reg = HIWORD_UPDATE_MASK(PHY_CFG_WR_ENABLE,
+				 PHY_CFG_WR_MASK,
+				 PHY_CFG_WR_SHIFT);
+	writel(reg, phy->reg_base + PCIE_PHY_CONF);
+
+	udelay(1);
+
+	reg = HIWORD_UPDATE_MASK(PHY_CFG_WR_DISABLE,
+				 PHY_CFG_WR_MASK,
+				 PHY_CFG_WR_SHIFT);
+	writel(reg, phy->reg_base + PCIE_PHY_CONF);
+}
+
+static int rockchip_pcie_phy_power_on(struct rockchip_pcie_phy *phy)
+{
+	int ret = 0;
+	u32 reg, status;
+
+	ret = reset_deassert(&phy->phy_rst);
+	if (ret) {
+		dev_err(dev, "failed to assert phy reset\n");
+		return ret;
+	}
+
+	reg = HIWORD_UPDATE_MASK(PHY_CFG_PLL_LOCK,
+				 PHY_CFG_ADDR_MASK,
+				 PHY_CFG_ADDR_SHIFT);
+	writel(reg, phy->reg_base + PCIE_PHY_CONF);
+
+	reg = HIWORD_UPDATE_MASK(!PHY_LANE_IDLE_OFF,
+				 PHY_LANE_IDLE_MASK,
+				 PHY_LANE_IDLE_A_SHIFT);
+	writel(reg, phy->reg_base + PCIE_PHY_LANEOFF);
+
+	ret = -EINVAL;
+	ret = readl_poll_sleep_timeout(phy->reg_base + PCIE_PHY_STATUS,
+				       status,
+				       status & PHY_PLL_LOCKED,
+				       20 * 1000,
+				       50);
+	if (ret) {
+		dev_err(&phy->dev, "pll lock timeout!\n");
+		goto err_pll_lock;
+	}
+
+	phy_wr_cfg(phy, PHY_CFG_CLK_TEST, PHY_CFG_SEPE_RATE);
+	phy_wr_cfg(phy, PHY_CFG_CLK_SCC, PHY_CFG_PLL_100M);
+
+	ret = -ETIMEDOUT;
+	ret = readl_poll_sleep_timeout(phy->reg_base + PCIE_PHY_STATUS,
+				       status,
+				       !(status & PHY_PLL_OUTPUT),
+				       20 * 1000,
+				       50);
+	if (ret) {
+		dev_err(&phy->dev, "pll output enable timeout!\n");
+		goto err_pll_lock;
+	}
+
+	reg = HIWORD_UPDATE_MASK(PHY_CFG_PLL_LOCK,
+				 PHY_CFG_ADDR_MASK,
+				 PHY_CFG_ADDR_SHIFT);
+	writel(reg, phy->reg_base + PCIE_PHY_CONF);
+
+	ret = -EINVAL;
+	ret = readl_poll_sleep_timeout(phy->reg_base + PCIE_PHY_STATUS,
+				       status,
+				       status & PHY_PLL_LOCKED,
+				       20 * 1000,
+				       50);
+	if (ret) {
+		dev_err(&phy->dev, "pll relock timeout!\n");
+		goto err_pll_lock;
+	}
+
+	return 0;
+
+err_pll_lock:
+	reset_assert(&phy->phy_rst);
+	return ret;
+}
+
+static int rockchip_pcie_phy_power_off(struct rockchip_pcie_phy *phy)
+{
+	int ret;
+	u32 reg;
+
+	reg = HIWORD_UPDATE_MASK(PHY_LANE_IDLE_OFF,
+				 PHY_LANE_IDLE_MASK,
+				 PHY_LANE_IDLE_A_SHIFT);
+	writel(reg, phy->reg_base + PCIE_PHY_LANEOFF);
+
+	ret = reset_assert(&phy->phy_rst);
+	if (ret) {
+		dev_err(dev, "failed to assert phy reset\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int rockchip_pcie_phy_init(struct rockchip_pcie_phy *phy)
+{
+	int ret;
+
+	ret = clk_enable(&phy->refclk);
+	if (ret) {
+		dev_err(dev, "failed to enable refclk clock\n");
+		return ret;
+	}
+
+	ret = reset_assert(&phy->phy_rst);
+	if (ret) {
+		dev_err(dev, "failed to assert phy reset\n");
+		goto err_reset;
+	}
+
+	return 0;
+
+err_reset:
+	clk_disable(&phy->refclk);
+	return ret;
+}
+
+static int rockchip_pcie_phy_exit(struct rockchip_pcie_phy *phy)
+{
+	clk_disable(&phy->refclk);
+
+	return 0;
+}
+
+static struct rockchip_pcie_phy_ops pcie_phy_ops = {
+	.init = rockchip_pcie_phy_init,
+	.power_on = rockchip_pcie_phy_power_on,
+	.power_off = rockchip_pcie_phy_power_off,
+	.exit = rockchip_pcie_phy_exit,
+};
+
+int rockchip_pcie_phy_get(struct udevice *dev)
+{
+	struct rockchip_pcie *priv = dev_get_priv(dev);
+	struct rockchip_pcie_phy *phy_priv = &priv->rk_phy;
+	ofnode phy_node;
+	u32 phandle;
+	int ret;
+
+	phandle = dev_read_u32_default(dev, "phys", 0);
+	phy_node = ofnode_get_by_phandle(phandle);
+	if (!ofnode_valid(phy_node)) {
+		dev_err(dev, "failed to found pcie-phy\n");
+		return -ENODEV;
+	}
+
+	phy_priv->reg_base = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
+
+	ret = clk_get_by_index_nodev(phy_node, 0, &phy_priv->refclk);
+	if (ret) {
+		dev_err(dev, "failed to get refclk clock phandle\n");
+		return ret;
+	}
+
+	ret = reset_get_by_index_nodev(phy_node, 0, &phy_priv->phy_rst);
+	if (ret) {
+		dev_err(dev, "failed to get phy reset phandle\n");
+		return ret;
+	}
+
+	phy_priv->ops = &pcie_phy_ops;
+	priv->phy = phy_priv;
+
+	return 0;
+}
