| // 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"); |