[Feature] add GA346 baseline version

Change-Id: Ic62933698569507dcf98240cdf5d9931ae34348f
diff --git a/src/kernel/linux/v4.19/drivers/phy/mediatek/phy-mtk-pcie.c b/src/kernel/linux/v4.19/drivers/phy/mediatek/phy-mtk-pcie.c
new file mode 100644
index 0000000..0133bee
--- /dev/null
+++ b/src/kernel/linux/v4.19/drivers/phy/mediatek/phy-mtk-pcie.c
@@ -0,0 +1,632 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 MediaTek Inc.
+ * Author: Jianjun Wang <jianjun.wang@mediatek.com>
+ *
+ */
+
+#include <dt-bindings/phy/phy.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/pm_domain.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/debugfs.h>
+#include <linux/of_device.h>
+
+#define PCIE_PHY_0_NUM			0
+#define PCIE_PHY_1_NUM			1
+#define PCIE_PHY_2_NUM			2
+#define PCIE_PHY_3_NUM			3
+#define PCIE_PHY_0_EFUSE_CNT		5
+#define PCIE_PHY_CKM_REG04		0x04
+#define PCIE_EFSUE_P1_INDEX		0x108
+#define PCIE_EFSUE_P2_INDEX		0x10C
+#define PCIE_EFSUE_P3_INDEX		0x110
+#define CKM_PT0_CKTX_IMPSEL(val)	((val) & GENMASK(3, 0))
+#define CKM_PT0_CKTX_R50_EN		BIT(5)
+#define CKM_PT0_CKTX_SLEW(val)		((val << 8) & GENMASK(9, 8))
+#define CKM_PT0_CKTX_OFFSET(val)	((val << 10) & GENMASK(11, 10))
+#define CKM_PT0_CKTX_AMP(val)		((val << 12) & GENMASK(15, 12))
+
+#define PEXTP_GLB_00_RG			0x9000
+#define PEXTP_04_RG			0xA004
+#define PEXTP_3C_RG			0xA03C
+#define PEXTP_104_RG			0xA104
+#define PEXTP_13C_RG			0xA13c
+
+#define EFUSE_FIELD_SIZE		4
+#define EFUSE_SHIFT(val, right, left, left_phy) \
+		((((val) & GENMASK(right, left)) >> (left)) << (left_phy))
+
+struct mtk_pcie_phy {
+	struct device *dev;
+	struct phy *phy;
+	void __iomem *sif_base;
+	void __iomem *ckm_base;
+	struct clk *pipe_clk;	/* pipe clock to mac */
+	enum phy_mode mode;
+};
+
+static const unsigned int efuse_field_offset[PCIE_PHY_0_EFUSE_CNT] = {
+	0x100, 0x104, 0x1B4, 0x1BC, 0x1C0
+};
+
+/*
+ * MT6890 RC portA is unlike the other ports, needs to get
+ * impedance select values from the efuse's multiple registers.
+ */
+static int pcie_fetch_efuse_scatter(struct mtk_pcie_phy *pcie_phy)
+{
+	struct nvmem_device *nvmem_dev;
+	u32 efuse_addr[PCIE_PHY_0_EFUSE_CNT] = {0}, val;
+	int ret;
+	int i;
+
+	nvmem_dev = nvmem_device_get(pcie_phy->dev, "mtk_efuse");
+	if (IS_ERR(nvmem_dev)) {
+		if (PTR_ERR(nvmem_dev) == -EPROBE_DEFER)
+			return PTR_ERR(nvmem_dev);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < PCIE_PHY_0_EFUSE_CNT; i++) {
+		ret = nvmem_device_read(nvmem_dev, efuse_field_offset[i],
+					   EFUSE_FIELD_SIZE, &efuse_addr[i]);
+		if (ret != EFUSE_FIELD_SIZE) {
+			dev_warn(pcie_phy->dev, "pcie efuse val is error\n");
+			return -EINVAL;
+		}
+	}
+	
+	if (efuse_addr[0] == 0) {
+		dev_err(pcie_phy->dev, "Failed to read efuse value\n");
+		return -EINVAL;
+	}
+
+	/* PEXTP_GLB_00_RG[28:24] Internal RG Selection of TX Bias Current */
+	val = readl(pcie_phy->sif_base + PEXTP_GLB_00_RG);
+	val = (val & ~GENMASK(28, 24)) | EFUSE_SHIFT(efuse_addr[0], 27, 23, 24);
+	writel(val, pcie_phy->sif_base + PEXTP_GLB_00_RG);
+
+	/* PCIE_PHY_CKM_REG04[3:0] Internal R50 impedance select */
+	val = readl(pcie_phy->ckm_base + PCIE_PHY_CKM_REG04);
+	val = (val & ~GENMASK(3, 0)) | EFUSE_SHIFT(efuse_addr[1], 8, 5, 0);
+	writel(val, pcie_phy->ckm_base + PCIE_PHY_CKM_REG04);
+
+	/* PEXTP_04_RG[5:2] LN0 TX PMOS impedance selection */
+	val = readl(pcie_phy->sif_base + PEXTP_04_RG);
+	val = (val & ~GENMASK(5, 2)) | EFUSE_SHIFT(efuse_addr[2], 4, 1, 2);
+	writel(val, pcie_phy->sif_base + PEXTP_04_RG);
+
+	/* PEXTP_104_RG[5:2] LN1 TX PMOS impedance selection */
+	val = readl(pcie_phy->sif_base + PEXTP_104_RG);
+	val = (val & ~GENMASK(5, 2)) | EFUSE_SHIFT(efuse_addr[2], 8, 5, 2);
+	writel(val, pcie_phy->sif_base + PEXTP_104_RG);
+
+	/* PEXTP_04_RG[11:8] LN0 TX NMOS impedance selection */
+	val = readl(pcie_phy->sif_base + PEXTP_04_RG);
+	val = (val & ~GENMASK(11, 8)) | EFUSE_SHIFT(efuse_addr[2], 20, 17, 8);
+	writel(val, pcie_phy->sif_base + PEXTP_04_RG);
+
+	/* PEXTP_104_RG[11:8] LN1 TX NMOS impedance selection */
+	val = readl(pcie_phy->sif_base + PEXTP_104_RG);
+	val = (val & ~GENMASK(11, 8)) | EFUSE_SHIFT(efuse_addr[2], 24, 21, 8);
+	writel(val, pcie_phy->sif_base + PEXTP_104_RG);
+
+	/* PEXTP_3C_RG[3:0] LN0 RX impedance selection */
+	val = readl(pcie_phy->sif_base + PEXTP_3C_RG);
+	val = (val & ~GENMASK(3, 0)) | EFUSE_SHIFT(efuse_addr[3], 31, 28, 0);
+	writel(val, pcie_phy->sif_base + PEXTP_3C_RG);
+
+	/* PEXTP_13C_RG[3:0] LN1 RX impedance selection */
+	val = readl(pcie_phy->sif_base + PEXTP_13C_RG);
+	val = (val & ~GENMASK(3, 0)) | EFUSE_SHIFT(efuse_addr[4], 3, 0, 0);
+	writel(val, pcie_phy->sif_base + PEXTP_13C_RG);
+
+	nvmem_device_put(nvmem_dev);
+
+	return 0;
+}
+
+static int pcie_fetch_efuse_unify(struct mtk_pcie_phy *pcie_phy)
+{
+	struct nvmem_device *nvmem_dev;
+	u32 efuse_addr = 0, val;
+	unsigned int efuse_index_offset = 0;
+	int ret;
+
+	switch (pcie_phy->phy->id) {
+	case PCIE_PHY_1_NUM:
+		efuse_index_offset = PCIE_EFSUE_P1_INDEX;
+		break;
+	case PCIE_PHY_2_NUM:
+		efuse_index_offset = PCIE_EFSUE_P2_INDEX;
+		break;
+	case PCIE_PHY_3_NUM:
+		efuse_index_offset = PCIE_EFSUE_P3_INDEX;
+		break;
+	default:
+		dev_info(pcie_phy->dev, "pcie phy num for efuse is error\n");
+	}
+
+	nvmem_dev = nvmem_device_get(pcie_phy->dev, "mtk_efuse");
+	if (IS_ERR(nvmem_dev)) {
+		if (PTR_ERR(nvmem_dev) == -EPROBE_DEFER)
+			return PTR_ERR(nvmem_dev);
+		return -EINVAL;
+	}
+
+	ret = nvmem_device_read(nvmem_dev, efuse_index_offset,
+				 EFUSE_FIELD_SIZE, &efuse_addr);
+	if (ret != EFUSE_FIELD_SIZE) {
+		dev_warn(pcie_phy->dev, "pcie efuse val is error\n");
+		return -EINVAL;
+	}
+	
+	if (efuse_addr == 0) {
+		dev_err(pcie_phy->dev, "Failed to read efuse value\n");
+		return -EINVAL;
+	}
+
+	/* PEXTP_3C_RG[3:0] LN0 RX impedance selection */
+	val = readl(pcie_phy->sif_base + PEXTP_3C_RG);
+	val = (val & ~GENMASK(3, 0)) | EFUSE_SHIFT(efuse_addr, 3, 0, 0);
+	writel(val, pcie_phy->sif_base + PEXTP_3C_RG);
+
+	/* PEXTP_04_RG[5:2] LN0 TX PMOS impedance selection */
+	val = readl(pcie_phy->sif_base + PEXTP_04_RG);
+	val = (val & ~GENMASK(5, 2)) | EFUSE_SHIFT(efuse_addr, 7, 4, 2);
+	writel(val, pcie_phy->sif_base + PEXTP_04_RG);
+
+	/* PEXTP_04_RG[11:8] LN0 TX NMOS impedance selection */
+	val = readl(pcie_phy->sif_base + PEXTP_04_RG);
+	val = (val & ~GENMASK(11, 8)) | EFUSE_SHIFT(efuse_addr, 11, 8, 8);
+	writel(val, pcie_phy->sif_base + PEXTP_04_RG);
+
+	/* PEXTP_GLB_00_RG[28:24] Internal RG Selection of TX Bias Current */
+	val = readl(pcie_phy->sif_base + PEXTP_GLB_00_RG);
+	val = (val & ~GENMASK(28, 24)) | EFUSE_SHIFT(efuse_addr, 16, 12, 24);
+	writel(val, pcie_phy->sif_base + PEXTP_GLB_00_RG);
+
+	/* portB is two lanes */
+	if (pcie_phy->phy->id == 1) {
+		/* PEXTP_13C_RG[3:0] LN1 RX impedance selection */
+		val = readl(pcie_phy->sif_base + PEXTP_13C_RG);
+		val = (val & ~GENMASK(3, 0)) |
+					EFUSE_SHIFT(efuse_addr, 20, 17, 0);
+		writel(val, pcie_phy->sif_base + PEXTP_13C_RG);
+
+		/* PEXTP_104_RG[5:2] LN1 TX PMOS impedance selection */
+		val = readl(pcie_phy->sif_base + PEXTP_104_RG);
+		val = (val & ~GENMASK(5, 2)) |
+					EFUSE_SHIFT(efuse_addr, 24, 21, 2);
+		writel(val, pcie_phy->sif_base + PEXTP_104_RG);
+
+		/* PEXTP_104_RG[11:8] LN1 TX NMOS impedance selection */
+		val = readl(pcie_phy->sif_base + PEXTP_104_RG);
+		val = (val & ~GENMASK(11, 8)) |
+					EFUSE_SHIFT(efuse_addr, 28, 25, 8);
+		writel(val, pcie_phy->sif_base + PEXTP_104_RG);
+	}
+
+	nvmem_device_put(nvmem_dev);
+
+	return 0;
+}
+
+static int mtk_pcie_phy_init(struct phy *phy)
+{
+	struct mtk_pcie_phy *pcie_phy = phy_get_drvdata(phy);
+	struct device *dev = pcie_phy->dev;
+	struct generic_pm_domain *pcie_pd;
+	int err;
+
+	if (pcie_phy->mode == PHY_MODE_PCIE_EP) {
+		dev_info(pcie_phy->dev, "set phy to EP mode\n");
+		if (dev->pm_domain) {
+			pcie_pd = pd_to_genpd(dev->pm_domain);
+			pcie_pd->flags |= GENPD_FLAG_ALWAYS_ON;
+		}
+	}
+
+	if (pcie_phy->mode == PHY_MODE_PCIE_RC) {
+		if (pcie_phy->phy->id == PCIE_PHY_0_NUM) {
+			err = pcie_fetch_efuse_scatter(pcie_phy);
+			if (err)
+				return -EINVAL;
+		}
+
+		if (pcie_phy->phy->id > PCIE_PHY_0_NUM) {
+			err = pcie_fetch_efuse_unify(pcie_phy);
+			if (err)
+				return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int mtk_pcie_phy_power_on(struct phy *phy)
+{
+	struct mtk_pcie_phy *pcie_phy = phy_get_drvdata(phy);
+	int val, ret;
+
+	pm_runtime_get_sync(pcie_phy->dev);
+
+	if (pcie_phy->mode == PHY_MODE_PCIE_RC) {
+		val = readl(pcie_phy->ckm_base + PCIE_PHY_CKM_REG04);
+		val &= ~GENMASK(11, 10);
+		val |= CKM_PT0_CKTX_OFFSET(0x2);
+		writel(val, pcie_phy->ckm_base + PCIE_PHY_CKM_REG04);
+	}
+
+	/* provide pipe clock to pcie mac */
+	if (pcie_phy->pipe_clk) {
+		ret = clk_prepare_enable(pcie_phy->pipe_clk);
+		if (ret) {
+			dev_err(pcie_phy->dev, "failed to enable pipe_clk\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int mtk_pcie_phy_power_off(struct phy *phy)
+{
+	struct mtk_pcie_phy *pcie_phy = phy_get_drvdata(phy);
+
+	if (pcie_phy->pipe_clk)
+		clk_disable_unprepare(pcie_phy->pipe_clk);
+
+	pm_runtime_put_sync(pcie_phy->dev);
+
+	return 0;
+}
+
+static int mtk_pcie_phy_exit(struct phy *phy)
+{
+	return 0;
+}
+
+static int mtk_pcie_phy_set_mode(struct phy *phy, enum phy_mode mode)
+{
+	struct mtk_pcie_phy *pcie_phy = phy_get_drvdata(phy);
+
+	pcie_phy->mode = mode;
+
+	return 0;
+}
+
+static const struct phy_ops mtk_pcie_phy_ops = {
+	.init		= mtk_pcie_phy_init,
+	.exit		= mtk_pcie_phy_exit,
+	.power_on	= mtk_pcie_phy_power_on,
+	.power_off	= mtk_pcie_phy_power_off,
+	.set_mode	= mtk_pcie_phy_set_mode,
+	.owner		= THIS_MODULE,
+};
+
+static struct phy *mtk_pcie_phy_xlate(struct device *dev,
+					struct of_phandle_args *args)
+{
+	struct mtk_pcie_phy *pcie_phy = dev_get_drvdata(dev);
+
+	if (!pcie_phy)
+		return NULL;
+
+	return pcie_phy->phy;
+}
+
+
+//tianyan@2021.10.21 modify for pcie loopback test start
+
+static void pcie_loopback_save_reg(void __iomem *phy_base,int *reg,int *reg_value,int len,bool flag)
+{
+	int i=0;
+
+	for(i=0; i<len; i++){
+		if(flag)
+			*(reg_value+i) = readl(phy_base + *reg++);
+		else
+			writel(*(reg_value+i),phy_base + *reg++);
+	}	
+}
+
+/* Loopback test for PCIe port
+ * Port 0 -> 2 lane
+ */
+static int pcie_loopback_test(void __iomem *phy_base)
+{
+	int val = 0, ret = 0;
+	int err_count = 0;
+	int reg[6]={0x28,0x70,0x4010,0x4110,0x501c,0x511c};
+	int reg_value[6]={0};
+
+	pr_info("pcie loopback test start\n");
+
+	if (!phy_base) {
+		pr_info("phy_base is not initialed!\n");
+		return -1;
+	}
+
+	pcie_loopback_save_reg(phy_base,reg,reg_value,6,true);
+
+	/* L1ss = enable */
+	val = readl(phy_base + 0x28);
+	val |= (0x01 << 5);
+	writel(val, phy_base + 0x28);
+
+	val = readl(phy_base + 0x28);
+	val |= (0x01 << 4);
+	writel(val, phy_base + 0x28);
+
+	val = readl(phy_base + 0x28);
+	val &= (~0x200);
+	writel(val, phy_base + 0x28);
+
+	val = readl(phy_base + 0x28);
+	val |= (0x01 << 8);
+	writel(val, phy_base + 0x28);
+
+	val = readl(phy_base + 0x28);
+	val &= (~0x800);
+	writel(val, phy_base + 0x28);
+
+	val = readl(phy_base + 0x28);
+	val |= (0x01 << 10);
+	writel(val, phy_base + 0x28);
+
+	/* Set Rate=Gen1 */
+	usleep_range(1, 2);
+	val = readl(phy_base + 0x70);
+	val |= (0x01);
+	writel(val, phy_base + 0x70);
+
+	val = readl(phy_base + 0x70);
+	val &= (~0x30000);
+	writel(val, phy_base + 0x70);
+
+	val = readl(phy_base + 0x70);
+	val |= (0x01 << 4);
+	writel(val, phy_base + 0x70);
+
+	usleep_range(1, 2);
+	val = readl(phy_base + 0x70);
+	val &= (~0x10);
+	writel(val, phy_base + 0x70);
+
+	/* Force PIPE (P0) */
+	val = readl(phy_base + 0x70);
+	val |= (0x01);
+	writel(val, phy_base + 0x70);
+
+	val = readl(phy_base + 0x70);
+	val &= (~0xc00);
+	writel(val, phy_base + 0x70);
+
+	val = readl(phy_base + 0x70);
+	val &= (~0x3000);
+	writel(val, phy_base + 0x70);
+
+	val = readl(phy_base + 0x70);
+	val |= (0x01 << 8);
+	writel(val, phy_base + 0x70);
+
+	val = readl(phy_base + 0x70);
+	val |= (0x01 << 4);
+	writel(val, phy_base + 0x70);
+
+	usleep_range(1, 2);
+	val = readl(phy_base + 0x70);
+	val &= (~0x10);
+	writel(val, phy_base + 0x70);
+
+	/* Set TX output Pattern */
+	usleep_range(1, 2);
+
+	/* Lane 0 */
+	val = readl(phy_base + 0x4010);
+	val |= (0x0d);
+	writel(val, phy_base + 0x4010);
+
+	/* Lane 1 */
+	val = readl(phy_base + 0x4110);
+	val |= (0x0d);
+	writel(val, phy_base + 0x4110);
+
+	/* Set TX PTG Enable */
+	val = readl(phy_base + 0x28);
+	val |= (0x01 << 30);
+	writel(val, phy_base + 0x28);
+
+	/* Set RX Pattern Checker (Type & Enable) */
+	/* Lane 0 */
+	val = readl(phy_base + 0x501c);
+	val |= (0x01 << 1);
+	writel(val, phy_base + 0x501c);
+
+	val = readl(phy_base + 0x501c);
+	val |= (0x0d << 4);
+	writel(val, phy_base + 0x501c);
+
+	/* Lane 1 */
+	val = readl(phy_base + 0x511c);
+	val |= (0x01 << 1);
+	writel(val, phy_base + 0x511c);
+
+	val = readl(phy_base + 0x511c);
+	val |= (0x0d << 4);
+	writel(val, phy_base + 0x511c);
+
+	/* toggle ptc_en for status counter clear */
+	/* Lane 0 */
+	val = readl(phy_base + 0x501c);
+	val &= (~0x2);
+	writel(val, phy_base + 0x501c);
+
+	val = readl(phy_base + 0x501c);
+	val |= (0x01 << 1);
+	writel(val, phy_base + 0x501c);
+
+	/* Lane 1 */
+	val = readl(phy_base + 0x511c);
+	val &= (~0x2);
+	writel(val, phy_base + 0x511c);
+
+	val = readl(phy_base + 0x511c);
+	val |= (0x01 << 1);
+	writel(val, phy_base + 0x511c);
+
+	/* Check status */
+	msleep(50);
+	val = readl(phy_base + 0x50c8);
+	if ((val & 0x3) != 0x3) {
+		err_count = val >> 12;
+		pr_info("PCIe test failed: %#x!\n", val);
+		pr_info("lane0 error count: %d\n", err_count);
+		ret = -1;
+	}
+
+	val = readl(phy_base + 0x51c8);
+	if ((val & 0x3) != 0x3) {
+		err_count = val >> 12;
+		pr_info("PCIe test failed: %#x!\n", val);
+		pr_info("lane1 error count: %d\n", err_count);
+		ret = -1;
+	}
+
+	pcie_loopback_save_reg(phy_base,reg,reg_value,6,false);
+	return ret;
+}
+
+static bool pcie_loopback_test_res=false;
+
+static int pcie_loopback_debugfs_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t pcie_loopback_debugfs_read(struct file *file, char __user *buf,
+				     size_t count, loff_t *pos)
+{
+	bool *result = file->private_data;
+	const int size = 128;
+	char *buffer = NULL; /* for reduce kernel stack */
+	int n = 0;
+	int ret = 0;
+
+	buffer = kmalloc(size, GFP_KERNEL);
+	if (!buffer || !buf)
+		return -ENOMEM;
+
+	if (!(*result)) {
+		pr_info("pcie loopback test failed!\n");
+		n += scnprintf(buffer + n, size - n, "pcie loopback test failed!\n");
+	}
+	else{
+		pr_info("pcie loopback test success!\n");
+		n += scnprintf(buffer + n, size - n, "pcie loopback test success!\n");
+	}
+
+	ret = simple_read_from_buffer(buf, count, pos, buffer, n);
+	kfree(buffer);
+	return ret;
+}
+
+
+static const struct file_operations pcie_loopback_debugfs_ops = {
+	.open = pcie_loopback_debugfs_open,
+	.read = pcie_loopback_debugfs_read,
+};
+
+//tianyan@2021.10.21 modify for pcie loopback test end
+
+static int mtk_pcie_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct phy_provider *provider;
+	struct resource *reg_res;
+	struct mtk_pcie_phy *pcie_phy;
+
+	pcie_phy = devm_kzalloc(dev, sizeof(*pcie_phy), GFP_KERNEL);
+	if (!pcie_phy)
+		return -ENOMEM;
+
+	pcie_phy->dev = dev;
+
+//tianyan@2021.7.27 modify for add wifi6 module start
+/*	pcie_phy->pipe_clk = devm_clk_get(pcie_phy->dev, "pipe_clk");
+	if (IS_ERR(pcie_phy->pipe_clk)) {
+		dev_warn(dev, "pcie pipe_clk not found\n");
+		pcie_phy->pipe_clk = NULL;
+	}*/
+//tianyan@2021.7.27 modify for add wifi6 module end
+
+	pm_runtime_enable(pcie_phy->dev);
+
+	reg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy-sif");
+	pcie_phy->sif_base = devm_ioremap_resource(dev, reg_res);
+	if (IS_ERR(pcie_phy->sif_base)) {
+		dev_warn(dev, "failed to map pcie phy base\n");
+		return PTR_ERR(pcie_phy->sif_base);
+	}
+	
+//tianyan@2021.10.21 modify for pcie loopback test start
+	if(!pcie_loopback_test(pcie_phy->sif_base)){
+		pcie_loopback_test_res = true;
+		pr_info("pcie loopback test success!\n");
+	}
+	(void*)debugfs_create_file("pcie_loopback",
+				    S_IFREG | 0444, NULL,
+				    &pcie_loopback_test_res, &pcie_loopback_debugfs_ops);
+//tianyan@2021.10.21 modify for pcie loopback test end
+
+	reg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy-ckm");
+	pcie_phy->ckm_base = devm_ioremap_resource(dev, reg_res);
+	if (IS_ERR(pcie_phy->ckm_base)) {
+		dev_warn(dev, "failed to map pcie phy base\n");
+		return PTR_ERR(pcie_phy->ckm_base);
+	}
+
+	platform_set_drvdata(pdev, pcie_phy);
+
+	pcie_phy->phy = devm_phy_create(dev, dev->of_node, &mtk_pcie_phy_ops);
+	if (IS_ERR(pcie_phy->phy)) {
+		dev_err(dev, "failed to create pcie phy\n");
+		return PTR_ERR(pcie_phy->phy);
+	}
+
+	phy_set_drvdata(pcie_phy->phy, pcie_phy);
+
+	provider = devm_of_phy_provider_register(dev, mtk_pcie_phy_xlate);
+
+	return PTR_ERR_OR_ZERO(provider);
+}
+
+static const struct of_device_id mtk_pcie_phy_id_table[] = {
+	{ .compatible = "mediatek,mt6880-pcie-phy" },
+	{ .compatible = "mediatek,mt2735-pcie-phy" },
+	{ },
+};
+
+static struct platform_driver mtk_pcie_phy_driver = {
+	.probe	= mtk_pcie_phy_probe,
+	.driver	= {
+		.name	= "mtk-pcie-phy",
+		.of_match_table = mtk_pcie_phy_id_table,
+	},
+};
+
+module_platform_driver(mtk_pcie_phy_driver);
+MODULE_LICENSE("GPL v2");