162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Mediatek MT7621 PCI PHY Driver 462306a36Sopenharmony_ci * Author: Sergio Paracuellos <sergio.paracuellos@gmail.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <dt-bindings/phy/phy.h> 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/bitfield.h> 1062306a36Sopenharmony_ci#include <linux/bitops.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/of.h> 1362306a36Sopenharmony_ci#include <linux/phy/phy.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci#include <linux/regmap.h> 1662306a36Sopenharmony_ci#include <linux/sys_soc.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define RG_PE1_PIPE_REG 0x02c 1962306a36Sopenharmony_ci#define RG_PE1_PIPE_RST BIT(12) 2062306a36Sopenharmony_ci#define RG_PE1_PIPE_CMD_FRC BIT(4) 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define RG_P0_TO_P1_WIDTH 0x100 2362306a36Sopenharmony_ci#define RG_PE1_H_LCDDS_REG 0x49c 2462306a36Sopenharmony_ci#define RG_PE1_H_LCDDS_PCW GENMASK(30, 0) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define RG_PE1_FRC_H_XTAL_REG 0x400 2762306a36Sopenharmony_ci#define RG_PE1_FRC_H_XTAL_TYPE BIT(8) 2862306a36Sopenharmony_ci#define RG_PE1_H_XTAL_TYPE GENMASK(10, 9) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define RG_PE1_FRC_PHY_REG 0x000 3162306a36Sopenharmony_ci#define RG_PE1_FRC_PHY_EN BIT(4) 3262306a36Sopenharmony_ci#define RG_PE1_PHY_EN BIT(5) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define RG_PE1_H_PLL_REG 0x490 3562306a36Sopenharmony_ci#define RG_PE1_H_PLL_BC GENMASK(23, 22) 3662306a36Sopenharmony_ci#define RG_PE1_H_PLL_BP GENMASK(21, 18) 3762306a36Sopenharmony_ci#define RG_PE1_H_PLL_IR GENMASK(15, 12) 3862306a36Sopenharmony_ci#define RG_PE1_H_PLL_IC GENMASK(11, 8) 3962306a36Sopenharmony_ci#define RG_PE1_H_PLL_PREDIV GENMASK(7, 6) 4062306a36Sopenharmony_ci#define RG_PE1_PLL_DIVEN GENMASK(3, 1) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define RG_PE1_H_PLL_FBKSEL_REG 0x4bc 4362306a36Sopenharmony_ci#define RG_PE1_H_PLL_FBKSEL GENMASK(5, 4) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define RG_PE1_H_LCDDS_SSC_PRD_REG 0x4a4 4662306a36Sopenharmony_ci#define RG_PE1_H_LCDDS_SSC_PRD GENMASK(15, 0) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define RG_PE1_H_LCDDS_SSC_DELTA_REG 0x4a8 4962306a36Sopenharmony_ci#define RG_PE1_H_LCDDS_SSC_DELTA GENMASK(11, 0) 5062306a36Sopenharmony_ci#define RG_PE1_H_LCDDS_SSC_DELTA1 GENMASK(27, 16) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define RG_PE1_LCDDS_CLK_PH_INV_REG 0x4a0 5362306a36Sopenharmony_ci#define RG_PE1_LCDDS_CLK_PH_INV BIT(5) 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#define RG_PE1_H_PLL_BR_REG 0x4ac 5662306a36Sopenharmony_ci#define RG_PE1_H_PLL_BR GENMASK(18, 16) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define RG_PE1_MSTCKDIV_REG 0x414 5962306a36Sopenharmony_ci#define RG_PE1_MSTCKDIV GENMASK(7, 6) 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define RG_PE1_FRC_MSTCKDIV BIT(5) 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define MAX_PHYS 2 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/** 6662306a36Sopenharmony_ci * struct mt7621_pci_phy - Mt7621 Pcie PHY core 6762306a36Sopenharmony_ci * @dev: pointer to device 6862306a36Sopenharmony_ci * @regmap: kernel regmap pointer 6962306a36Sopenharmony_ci * @phy: pointer to the kernel PHY device 7062306a36Sopenharmony_ci * @sys_clk: pointer to the system XTAL clock 7162306a36Sopenharmony_ci * @port_base: base register 7262306a36Sopenharmony_ci * @has_dual_port: if the phy has dual ports. 7362306a36Sopenharmony_ci * @bypass_pipe_rst: mark if 'mt7621_bypass_pipe_rst' 7462306a36Sopenharmony_ci * needs to be executed. Depends on chip revision. 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_cistruct mt7621_pci_phy { 7762306a36Sopenharmony_ci struct device *dev; 7862306a36Sopenharmony_ci struct regmap *regmap; 7962306a36Sopenharmony_ci struct phy *phy; 8062306a36Sopenharmony_ci struct clk *sys_clk; 8162306a36Sopenharmony_ci void __iomem *port_base; 8262306a36Sopenharmony_ci bool has_dual_port; 8362306a36Sopenharmony_ci bool bypass_pipe_rst; 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic inline void mt7621_phy_rmw(struct mt7621_pci_phy *phy, 8762306a36Sopenharmony_ci u32 reg, u32 clr, u32 set) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci u32 val; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* 9262306a36Sopenharmony_ci * We cannot use 'regmap_write_bits' here because internally 9362306a36Sopenharmony_ci * 'set' is masked before is set to the value that will be 9462306a36Sopenharmony_ci * written to the register. That way results in no reliable 9562306a36Sopenharmony_ci * pci setup. Avoid to mask 'set' before set value to 'val' 9662306a36Sopenharmony_ci * completely avoid the problem. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci regmap_read(phy->regmap, reg, &val); 9962306a36Sopenharmony_ci val &= ~clr; 10062306a36Sopenharmony_ci val |= set; 10162306a36Sopenharmony_ci regmap_write(phy->regmap, reg, val); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic void mt7621_bypass_pipe_rst(struct mt7621_pci_phy *phy) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci mt7621_phy_rmw(phy, RG_PE1_PIPE_REG, 0, RG_PE1_PIPE_RST); 10762306a36Sopenharmony_ci mt7621_phy_rmw(phy, RG_PE1_PIPE_REG, 0, RG_PE1_PIPE_CMD_FRC); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (phy->has_dual_port) { 11062306a36Sopenharmony_ci mt7621_phy_rmw(phy, RG_PE1_PIPE_REG + RG_P0_TO_P1_WIDTH, 11162306a36Sopenharmony_ci 0, RG_PE1_PIPE_RST); 11262306a36Sopenharmony_ci mt7621_phy_rmw(phy, RG_PE1_PIPE_REG + RG_P0_TO_P1_WIDTH, 11362306a36Sopenharmony_ci 0, RG_PE1_PIPE_CMD_FRC); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int mt7621_set_phy_for_ssc(struct mt7621_pci_phy *phy) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct device *dev = phy->dev; 12062306a36Sopenharmony_ci unsigned long clk_rate; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci clk_rate = clk_get_rate(phy->sys_clk); 12362306a36Sopenharmony_ci if (!clk_rate) 12462306a36Sopenharmony_ci return -EINVAL; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* Set PCIe Port PHY to disable SSC */ 12762306a36Sopenharmony_ci /* Debug Xtal Type */ 12862306a36Sopenharmony_ci mt7621_phy_rmw(phy, RG_PE1_FRC_H_XTAL_REG, 12962306a36Sopenharmony_ci RG_PE1_FRC_H_XTAL_TYPE | RG_PE1_H_XTAL_TYPE, 13062306a36Sopenharmony_ci RG_PE1_FRC_H_XTAL_TYPE | 13162306a36Sopenharmony_ci FIELD_PREP(RG_PE1_H_XTAL_TYPE, 0x00)); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* disable port */ 13462306a36Sopenharmony_ci mt7621_phy_rmw(phy, RG_PE1_FRC_PHY_REG, RG_PE1_PHY_EN, 13562306a36Sopenharmony_ci RG_PE1_FRC_PHY_EN); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (phy->has_dual_port) { 13862306a36Sopenharmony_ci mt7621_phy_rmw(phy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH, 13962306a36Sopenharmony_ci RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN); 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (clk_rate == 40000000) { /* 40MHz Xtal */ 14362306a36Sopenharmony_ci /* Set Pre-divider ratio (for host mode) */ 14462306a36Sopenharmony_ci mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, RG_PE1_H_PLL_PREDIV, 14562306a36Sopenharmony_ci FIELD_PREP(RG_PE1_H_PLL_PREDIV, 0x01)); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci dev_dbg(dev, "Xtal is 40MHz\n"); 14862306a36Sopenharmony_ci } else if (clk_rate == 25000000) { /* 25MHz Xal */ 14962306a36Sopenharmony_ci mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, RG_PE1_H_PLL_PREDIV, 15062306a36Sopenharmony_ci FIELD_PREP(RG_PE1_H_PLL_PREDIV, 0x00)); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* Select feedback clock */ 15362306a36Sopenharmony_ci mt7621_phy_rmw(phy, RG_PE1_H_PLL_FBKSEL_REG, 15462306a36Sopenharmony_ci RG_PE1_H_PLL_FBKSEL, 15562306a36Sopenharmony_ci FIELD_PREP(RG_PE1_H_PLL_FBKSEL, 0x01)); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* DDS NCPO PCW (for host mode) */ 15862306a36Sopenharmony_ci mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_PRD_REG, 15962306a36Sopenharmony_ci RG_PE1_H_LCDDS_SSC_PRD, 16062306a36Sopenharmony_ci FIELD_PREP(RG_PE1_H_LCDDS_SSC_PRD, 0x00)); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* DDS SSC dither period control */ 16362306a36Sopenharmony_ci mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_PRD_REG, 16462306a36Sopenharmony_ci RG_PE1_H_LCDDS_SSC_PRD, 16562306a36Sopenharmony_ci FIELD_PREP(RG_PE1_H_LCDDS_SSC_PRD, 0x18d)); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* DDS SSC dither amplitude control */ 16862306a36Sopenharmony_ci mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_DELTA_REG, 16962306a36Sopenharmony_ci RG_PE1_H_LCDDS_SSC_DELTA | 17062306a36Sopenharmony_ci RG_PE1_H_LCDDS_SSC_DELTA1, 17162306a36Sopenharmony_ci FIELD_PREP(RG_PE1_H_LCDDS_SSC_DELTA, 0x4a) | 17262306a36Sopenharmony_ci FIELD_PREP(RG_PE1_H_LCDDS_SSC_DELTA1, 0x4a)); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci dev_dbg(dev, "Xtal is 25MHz\n"); 17562306a36Sopenharmony_ci } else { /* 20MHz Xtal */ 17662306a36Sopenharmony_ci mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, RG_PE1_H_PLL_PREDIV, 17762306a36Sopenharmony_ci FIELD_PREP(RG_PE1_H_PLL_PREDIV, 0x00)); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci dev_dbg(dev, "Xtal is 20MHz\n"); 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* DDS clock inversion */ 18362306a36Sopenharmony_ci mt7621_phy_rmw(phy, RG_PE1_LCDDS_CLK_PH_INV_REG, 18462306a36Sopenharmony_ci RG_PE1_LCDDS_CLK_PH_INV, RG_PE1_LCDDS_CLK_PH_INV); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* Set PLL bits */ 18762306a36Sopenharmony_ci mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, 18862306a36Sopenharmony_ci RG_PE1_H_PLL_BC | RG_PE1_H_PLL_BP | RG_PE1_H_PLL_IR | 18962306a36Sopenharmony_ci RG_PE1_H_PLL_IC | RG_PE1_PLL_DIVEN, 19062306a36Sopenharmony_ci FIELD_PREP(RG_PE1_H_PLL_BC, 0x02) | 19162306a36Sopenharmony_ci FIELD_PREP(RG_PE1_H_PLL_BP, 0x06) | 19262306a36Sopenharmony_ci FIELD_PREP(RG_PE1_H_PLL_IR, 0x02) | 19362306a36Sopenharmony_ci FIELD_PREP(RG_PE1_H_PLL_IC, 0x01) | 19462306a36Sopenharmony_ci FIELD_PREP(RG_PE1_PLL_DIVEN, 0x02)); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci mt7621_phy_rmw(phy, RG_PE1_H_PLL_BR_REG, RG_PE1_H_PLL_BR, 19762306a36Sopenharmony_ci FIELD_PREP(RG_PE1_H_PLL_BR, 0x00)); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (clk_rate == 40000000) { /* 40MHz Xtal */ 20062306a36Sopenharmony_ci /* set force mode enable of da_pe1_mstckdiv */ 20162306a36Sopenharmony_ci mt7621_phy_rmw(phy, RG_PE1_MSTCKDIV_REG, 20262306a36Sopenharmony_ci RG_PE1_MSTCKDIV | RG_PE1_FRC_MSTCKDIV, 20362306a36Sopenharmony_ci FIELD_PREP(RG_PE1_MSTCKDIV, 0x01) | 20462306a36Sopenharmony_ci RG_PE1_FRC_MSTCKDIV); 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return 0; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic int mt7621_pci_phy_init(struct phy *phy) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct mt7621_pci_phy *mphy = phy_get_drvdata(phy); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (mphy->bypass_pipe_rst) 21562306a36Sopenharmony_ci mt7621_bypass_pipe_rst(mphy); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci return mt7621_set_phy_for_ssc(mphy); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic int mt7621_pci_phy_power_on(struct phy *phy) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct mt7621_pci_phy *mphy = phy_get_drvdata(phy); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* Enable PHY and disable force mode */ 22562306a36Sopenharmony_ci mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG, 22662306a36Sopenharmony_ci RG_PE1_FRC_PHY_EN, RG_PE1_PHY_EN); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (mphy->has_dual_port) { 22962306a36Sopenharmony_ci mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH, 23062306a36Sopenharmony_ci RG_PE1_FRC_PHY_EN, RG_PE1_PHY_EN); 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return 0; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic int mt7621_pci_phy_power_off(struct phy *phy) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct mt7621_pci_phy *mphy = phy_get_drvdata(phy); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* Disable PHY */ 24162306a36Sopenharmony_ci mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG, 24262306a36Sopenharmony_ci RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (mphy->has_dual_port) { 24562306a36Sopenharmony_ci mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH, 24662306a36Sopenharmony_ci RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN); 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci return 0; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic int mt7621_pci_phy_exit(struct phy *phy) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci return 0; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic const struct phy_ops mt7621_pci_phy_ops = { 25862306a36Sopenharmony_ci .init = mt7621_pci_phy_init, 25962306a36Sopenharmony_ci .exit = mt7621_pci_phy_exit, 26062306a36Sopenharmony_ci .power_on = mt7621_pci_phy_power_on, 26162306a36Sopenharmony_ci .power_off = mt7621_pci_phy_power_off, 26262306a36Sopenharmony_ci .owner = THIS_MODULE, 26362306a36Sopenharmony_ci}; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic struct phy *mt7621_pcie_phy_of_xlate(struct device *dev, 26662306a36Sopenharmony_ci struct of_phandle_args *args) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci struct mt7621_pci_phy *mt7621_phy = dev_get_drvdata(dev); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (WARN_ON(args->args[0] >= MAX_PHYS)) 27162306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci mt7621_phy->has_dual_port = args->args[0]; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci dev_dbg(dev, "PHY for 0x%px (dual port = %d)\n", 27662306a36Sopenharmony_ci mt7621_phy->port_base, mt7621_phy->has_dual_port); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return mt7621_phy->phy; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic const struct soc_device_attribute mt7621_pci_quirks_match[] = { 28262306a36Sopenharmony_ci { .soc_id = "mt7621", .revision = "E2" }, 28362306a36Sopenharmony_ci { /* sentinel */ } 28462306a36Sopenharmony_ci}; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic const struct regmap_config mt7621_pci_phy_regmap_config = { 28762306a36Sopenharmony_ci .reg_bits = 32, 28862306a36Sopenharmony_ci .val_bits = 32, 28962306a36Sopenharmony_ci .reg_stride = 4, 29062306a36Sopenharmony_ci .max_register = 0x700, 29162306a36Sopenharmony_ci}; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic int mt7621_pci_phy_probe(struct platform_device *pdev) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct device *dev = &pdev->dev; 29662306a36Sopenharmony_ci const struct soc_device_attribute *attr; 29762306a36Sopenharmony_ci struct phy_provider *provider; 29862306a36Sopenharmony_ci struct mt7621_pci_phy *phy; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); 30162306a36Sopenharmony_ci if (!phy) 30262306a36Sopenharmony_ci return -ENOMEM; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci attr = soc_device_match(mt7621_pci_quirks_match); 30562306a36Sopenharmony_ci if (attr) 30662306a36Sopenharmony_ci phy->bypass_pipe_rst = true; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci phy->dev = dev; 30962306a36Sopenharmony_ci platform_set_drvdata(pdev, phy); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci phy->port_base = devm_platform_ioremap_resource(pdev, 0); 31262306a36Sopenharmony_ci if (IS_ERR(phy->port_base)) { 31362306a36Sopenharmony_ci dev_err(dev, "failed to remap phy regs\n"); 31462306a36Sopenharmony_ci return PTR_ERR(phy->port_base); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci phy->regmap = devm_regmap_init_mmio(phy->dev, phy->port_base, 31862306a36Sopenharmony_ci &mt7621_pci_phy_regmap_config); 31962306a36Sopenharmony_ci if (IS_ERR(phy->regmap)) 32062306a36Sopenharmony_ci return PTR_ERR(phy->regmap); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci phy->phy = devm_phy_create(dev, dev->of_node, &mt7621_pci_phy_ops); 32362306a36Sopenharmony_ci if (IS_ERR(phy->phy)) { 32462306a36Sopenharmony_ci dev_err(dev, "failed to create phy\n"); 32562306a36Sopenharmony_ci return PTR_ERR(phy->phy); 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci phy->sys_clk = devm_clk_get(dev, NULL); 32962306a36Sopenharmony_ci if (IS_ERR(phy->sys_clk)) { 33062306a36Sopenharmony_ci dev_err(dev, "failed to get phy clock\n"); 33162306a36Sopenharmony_ci return PTR_ERR(phy->sys_clk); 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci phy_set_drvdata(phy->phy, phy); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci provider = devm_of_phy_provider_register(dev, mt7621_pcie_phy_of_xlate); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(provider); 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic const struct of_device_id mt7621_pci_phy_ids[] = { 34262306a36Sopenharmony_ci { .compatible = "mediatek,mt7621-pci-phy" }, 34362306a36Sopenharmony_ci {}, 34462306a36Sopenharmony_ci}; 34562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mt7621_pci_phy_ids); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic struct platform_driver mt7621_pci_phy_driver = { 34862306a36Sopenharmony_ci .probe = mt7621_pci_phy_probe, 34962306a36Sopenharmony_ci .driver = { 35062306a36Sopenharmony_ci .name = "mt7621-pci-phy", 35162306a36Sopenharmony_ci .of_match_table = mt7621_pci_phy_ids, 35262306a36Sopenharmony_ci }, 35362306a36Sopenharmony_ci}; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cibuiltin_platform_driver(mt7621_pci_phy_driver); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ciMODULE_AUTHOR("Sergio Paracuellos <sergio.paracuellos@gmail.com>"); 35862306a36Sopenharmony_ciMODULE_DESCRIPTION("MediaTek MT7621 PCIe PHY driver"); 35962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 360