| // 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> | 
 | #include <linux/gpio.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! GPIO97 output high\n"); | 
 | 		gpio_direction_output(268+97,1); | 
 | 	} | 
 | 	(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"); |