1d87da323SSergio Paracuellos // SPDX-License-Identifier: GPL-2.0+ 2d87da323SSergio Paracuellos /* 3d87da323SSergio Paracuellos * Mediatek MT7621 PCI PHY Driver 4d87da323SSergio Paracuellos * Author: Sergio Paracuellos <sergio.paracuellos@gmail.com> 5d87da323SSergio Paracuellos */ 6d87da323SSergio Paracuellos 7d87da323SSergio Paracuellos #include <dt-bindings/phy/phy.h> 8*60ece833SSergio Paracuellos #include <linux/clk.h> 9d87da323SSergio Paracuellos #include <linux/bitfield.h> 10d87da323SSergio Paracuellos #include <linux/bitops.h> 11d87da323SSergio Paracuellos #include <linux/module.h> 12d87da323SSergio Paracuellos #include <linux/of_address.h> 13d87da323SSergio Paracuellos #include <linux/of_device.h> 14d87da323SSergio Paracuellos #include <linux/phy/phy.h> 15d87da323SSergio Paracuellos #include <linux/platform_device.h> 16d87da323SSergio Paracuellos #include <linux/regmap.h> 17d87da323SSergio Paracuellos #include <linux/sys_soc.h> 18d87da323SSergio Paracuellos 19d87da323SSergio Paracuellos #define RG_PE1_PIPE_REG 0x02c 20d87da323SSergio Paracuellos #define RG_PE1_PIPE_RST BIT(12) 21d87da323SSergio Paracuellos #define RG_PE1_PIPE_CMD_FRC BIT(4) 22d87da323SSergio Paracuellos 23d87da323SSergio Paracuellos #define RG_P0_TO_P1_WIDTH 0x100 24d87da323SSergio Paracuellos #define RG_PE1_H_LCDDS_REG 0x49c 25d87da323SSergio Paracuellos #define RG_PE1_H_LCDDS_PCW GENMASK(30, 0) 26d87da323SSergio Paracuellos 27d87da323SSergio Paracuellos #define RG_PE1_FRC_H_XTAL_REG 0x400 28d87da323SSergio Paracuellos #define RG_PE1_FRC_H_XTAL_TYPE BIT(8) 29d87da323SSergio Paracuellos #define RG_PE1_H_XTAL_TYPE GENMASK(10, 9) 30d87da323SSergio Paracuellos 31d87da323SSergio Paracuellos #define RG_PE1_FRC_PHY_REG 0x000 32d87da323SSergio Paracuellos #define RG_PE1_FRC_PHY_EN BIT(4) 33d87da323SSergio Paracuellos #define RG_PE1_PHY_EN BIT(5) 34d87da323SSergio Paracuellos 35d87da323SSergio Paracuellos #define RG_PE1_H_PLL_REG 0x490 36d87da323SSergio Paracuellos #define RG_PE1_H_PLL_BC GENMASK(23, 22) 37d87da323SSergio Paracuellos #define RG_PE1_H_PLL_BP GENMASK(21, 18) 38d87da323SSergio Paracuellos #define RG_PE1_H_PLL_IR GENMASK(15, 12) 39d87da323SSergio Paracuellos #define RG_PE1_H_PLL_IC GENMASK(11, 8) 40d87da323SSergio Paracuellos #define RG_PE1_H_PLL_PREDIV GENMASK(7, 6) 41d87da323SSergio Paracuellos #define RG_PE1_PLL_DIVEN GENMASK(3, 1) 42d87da323SSergio Paracuellos 43d87da323SSergio Paracuellos #define RG_PE1_H_PLL_FBKSEL_REG 0x4bc 44d87da323SSergio Paracuellos #define RG_PE1_H_PLL_FBKSEL GENMASK(5, 4) 45d87da323SSergio Paracuellos 46d87da323SSergio Paracuellos #define RG_PE1_H_LCDDS_SSC_PRD_REG 0x4a4 47d87da323SSergio Paracuellos #define RG_PE1_H_LCDDS_SSC_PRD GENMASK(15, 0) 48d87da323SSergio Paracuellos 49d87da323SSergio Paracuellos #define RG_PE1_H_LCDDS_SSC_DELTA_REG 0x4a8 50d87da323SSergio Paracuellos #define RG_PE1_H_LCDDS_SSC_DELTA GENMASK(11, 0) 51d87da323SSergio Paracuellos #define RG_PE1_H_LCDDS_SSC_DELTA1 GENMASK(27, 16) 52d87da323SSergio Paracuellos 53d87da323SSergio Paracuellos #define RG_PE1_LCDDS_CLK_PH_INV_REG 0x4a0 54d87da323SSergio Paracuellos #define RG_PE1_LCDDS_CLK_PH_INV BIT(5) 55d87da323SSergio Paracuellos 56d87da323SSergio Paracuellos #define RG_PE1_H_PLL_BR_REG 0x4ac 57d87da323SSergio Paracuellos #define RG_PE1_H_PLL_BR GENMASK(18, 16) 58d87da323SSergio Paracuellos 59d87da323SSergio Paracuellos #define RG_PE1_MSTCKDIV_REG 0x414 60d87da323SSergio Paracuellos #define RG_PE1_MSTCKDIV GENMASK(7, 6) 61d87da323SSergio Paracuellos 62d87da323SSergio Paracuellos #define RG_PE1_FRC_MSTCKDIV BIT(5) 63d87da323SSergio Paracuellos 64d87da323SSergio Paracuellos #define MAX_PHYS 2 65d87da323SSergio Paracuellos 66d87da323SSergio Paracuellos /** 67d87da323SSergio Paracuellos * struct mt7621_pci_phy - Mt7621 Pcie PHY core 68d87da323SSergio Paracuellos * @dev: pointer to device 69d87da323SSergio Paracuellos * @regmap: kernel regmap pointer 70d87da323SSergio Paracuellos * @phy: pointer to the kernel PHY device 71*60ece833SSergio Paracuellos * @sys_clk: pointer to the system XTAL clock 72d87da323SSergio Paracuellos * @port_base: base register 73d87da323SSergio Paracuellos * @has_dual_port: if the phy has dual ports. 74d87da323SSergio Paracuellos * @bypass_pipe_rst: mark if 'mt7621_bypass_pipe_rst' 75d87da323SSergio Paracuellos * needs to be executed. Depends on chip revision. 76d87da323SSergio Paracuellos */ 77d87da323SSergio Paracuellos struct mt7621_pci_phy { 78d87da323SSergio Paracuellos struct device *dev; 79d87da323SSergio Paracuellos struct regmap *regmap; 80d87da323SSergio Paracuellos struct phy *phy; 81*60ece833SSergio Paracuellos struct clk *sys_clk; 82d87da323SSergio Paracuellos void __iomem *port_base; 83d87da323SSergio Paracuellos bool has_dual_port; 84d87da323SSergio Paracuellos bool bypass_pipe_rst; 85d87da323SSergio Paracuellos }; 86d87da323SSergio Paracuellos 87d87da323SSergio Paracuellos static inline void mt7621_phy_rmw(struct mt7621_pci_phy *phy, 88d87da323SSergio Paracuellos u32 reg, u32 clr, u32 set) 89d87da323SSergio Paracuellos { 90d87da323SSergio Paracuellos u32 val; 91d87da323SSergio Paracuellos 92d87da323SSergio Paracuellos /* 93d87da323SSergio Paracuellos * We cannot use 'regmap_write_bits' here because internally 94d87da323SSergio Paracuellos * 'set' is masked before is set to the value that will be 95d87da323SSergio Paracuellos * written to the register. That way results in no reliable 96d87da323SSergio Paracuellos * pci setup. Avoid to mask 'set' before set value to 'val' 97d87da323SSergio Paracuellos * completely avoid the problem. 98d87da323SSergio Paracuellos */ 99d87da323SSergio Paracuellos regmap_read(phy->regmap, reg, &val); 100d87da323SSergio Paracuellos val &= ~clr; 101d87da323SSergio Paracuellos val |= set; 102d87da323SSergio Paracuellos regmap_write(phy->regmap, reg, val); 103d87da323SSergio Paracuellos } 104d87da323SSergio Paracuellos 105d87da323SSergio Paracuellos static void mt7621_bypass_pipe_rst(struct mt7621_pci_phy *phy) 106d87da323SSergio Paracuellos { 107d87da323SSergio Paracuellos mt7621_phy_rmw(phy, RG_PE1_PIPE_REG, 0, RG_PE1_PIPE_RST); 108d87da323SSergio Paracuellos mt7621_phy_rmw(phy, RG_PE1_PIPE_REG, 0, RG_PE1_PIPE_CMD_FRC); 109d87da323SSergio Paracuellos 110d87da323SSergio Paracuellos if (phy->has_dual_port) { 111d87da323SSergio Paracuellos mt7621_phy_rmw(phy, RG_PE1_PIPE_REG + RG_P0_TO_P1_WIDTH, 112d87da323SSergio Paracuellos 0, RG_PE1_PIPE_RST); 113d87da323SSergio Paracuellos mt7621_phy_rmw(phy, RG_PE1_PIPE_REG + RG_P0_TO_P1_WIDTH, 114d87da323SSergio Paracuellos 0, RG_PE1_PIPE_CMD_FRC); 115d87da323SSergio Paracuellos } 116d87da323SSergio Paracuellos } 117d87da323SSergio Paracuellos 118*60ece833SSergio Paracuellos static int mt7621_set_phy_for_ssc(struct mt7621_pci_phy *phy) 119d87da323SSergio Paracuellos { 120d87da323SSergio Paracuellos struct device *dev = phy->dev; 121*60ece833SSergio Paracuellos unsigned long clk_rate; 122d87da323SSergio Paracuellos 123*60ece833SSergio Paracuellos clk_rate = clk_get_rate(phy->sys_clk); 124*60ece833SSergio Paracuellos if (!clk_rate) 125*60ece833SSergio Paracuellos return -EINVAL; 126d87da323SSergio Paracuellos 127d87da323SSergio Paracuellos /* Set PCIe Port PHY to disable SSC */ 128d87da323SSergio Paracuellos /* Debug Xtal Type */ 129d87da323SSergio Paracuellos mt7621_phy_rmw(phy, RG_PE1_FRC_H_XTAL_REG, 130d87da323SSergio Paracuellos RG_PE1_FRC_H_XTAL_TYPE | RG_PE1_H_XTAL_TYPE, 131d87da323SSergio Paracuellos RG_PE1_FRC_H_XTAL_TYPE | 132d87da323SSergio Paracuellos FIELD_PREP(RG_PE1_H_XTAL_TYPE, 0x00)); 133d87da323SSergio Paracuellos 134d87da323SSergio Paracuellos /* disable port */ 135d87da323SSergio Paracuellos mt7621_phy_rmw(phy, RG_PE1_FRC_PHY_REG, RG_PE1_PHY_EN, 136d87da323SSergio Paracuellos RG_PE1_FRC_PHY_EN); 137d87da323SSergio Paracuellos 138d87da323SSergio Paracuellos if (phy->has_dual_port) { 139d87da323SSergio Paracuellos mt7621_phy_rmw(phy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH, 140d87da323SSergio Paracuellos RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN); 141d87da323SSergio Paracuellos } 142d87da323SSergio Paracuellos 143*60ece833SSergio Paracuellos if (clk_rate == 40000000) { /* 40MHz Xtal */ 144d87da323SSergio Paracuellos /* Set Pre-divider ratio (for host mode) */ 145d87da323SSergio Paracuellos mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, RG_PE1_H_PLL_PREDIV, 146d87da323SSergio Paracuellos FIELD_PREP(RG_PE1_H_PLL_PREDIV, 0x01)); 147d87da323SSergio Paracuellos 148d87da323SSergio Paracuellos dev_dbg(dev, "Xtal is 40MHz\n"); 149*60ece833SSergio Paracuellos } else if (clk_rate == 25000000) { /* 25MHz Xal */ 150d87da323SSergio Paracuellos mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, RG_PE1_H_PLL_PREDIV, 151d87da323SSergio Paracuellos FIELD_PREP(RG_PE1_H_PLL_PREDIV, 0x00)); 152d87da323SSergio Paracuellos 153d87da323SSergio Paracuellos /* Select feedback clock */ 154d87da323SSergio Paracuellos mt7621_phy_rmw(phy, RG_PE1_H_PLL_FBKSEL_REG, 155d87da323SSergio Paracuellos RG_PE1_H_PLL_FBKSEL, 156d87da323SSergio Paracuellos FIELD_PREP(RG_PE1_H_PLL_FBKSEL, 0x01)); 157d87da323SSergio Paracuellos 158d87da323SSergio Paracuellos /* DDS NCPO PCW (for host mode) */ 159d87da323SSergio Paracuellos mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_PRD_REG, 160d87da323SSergio Paracuellos RG_PE1_H_LCDDS_SSC_PRD, 161d87da323SSergio Paracuellos FIELD_PREP(RG_PE1_H_LCDDS_SSC_PRD, 0x00)); 162d87da323SSergio Paracuellos 163d87da323SSergio Paracuellos /* DDS SSC dither period control */ 164d87da323SSergio Paracuellos mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_PRD_REG, 165d87da323SSergio Paracuellos RG_PE1_H_LCDDS_SSC_PRD, 166d87da323SSergio Paracuellos FIELD_PREP(RG_PE1_H_LCDDS_SSC_PRD, 0x18d)); 167d87da323SSergio Paracuellos 168d87da323SSergio Paracuellos /* DDS SSC dither amplitude control */ 169d87da323SSergio Paracuellos mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_DELTA_REG, 170d87da323SSergio Paracuellos RG_PE1_H_LCDDS_SSC_DELTA | 171d87da323SSergio Paracuellos RG_PE1_H_LCDDS_SSC_DELTA1, 172d87da323SSergio Paracuellos FIELD_PREP(RG_PE1_H_LCDDS_SSC_DELTA, 0x4a) | 173d87da323SSergio Paracuellos FIELD_PREP(RG_PE1_H_LCDDS_SSC_DELTA1, 0x4a)); 174d87da323SSergio Paracuellos 175d87da323SSergio Paracuellos dev_dbg(dev, "Xtal is 25MHz\n"); 176d87da323SSergio Paracuellos } else { /* 20MHz Xtal */ 177d87da323SSergio Paracuellos mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, RG_PE1_H_PLL_PREDIV, 178d87da323SSergio Paracuellos FIELD_PREP(RG_PE1_H_PLL_PREDIV, 0x00)); 179d87da323SSergio Paracuellos 180d87da323SSergio Paracuellos dev_dbg(dev, "Xtal is 20MHz\n"); 181d87da323SSergio Paracuellos } 182d87da323SSergio Paracuellos 183d87da323SSergio Paracuellos /* DDS clock inversion */ 184d87da323SSergio Paracuellos mt7621_phy_rmw(phy, RG_PE1_LCDDS_CLK_PH_INV_REG, 185d87da323SSergio Paracuellos RG_PE1_LCDDS_CLK_PH_INV, RG_PE1_LCDDS_CLK_PH_INV); 186d87da323SSergio Paracuellos 187d87da323SSergio Paracuellos /* Set PLL bits */ 188d87da323SSergio Paracuellos mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, 189d87da323SSergio Paracuellos RG_PE1_H_PLL_BC | RG_PE1_H_PLL_BP | RG_PE1_H_PLL_IR | 190d87da323SSergio Paracuellos RG_PE1_H_PLL_IC | RG_PE1_PLL_DIVEN, 191d87da323SSergio Paracuellos FIELD_PREP(RG_PE1_H_PLL_BC, 0x02) | 192d87da323SSergio Paracuellos FIELD_PREP(RG_PE1_H_PLL_BP, 0x06) | 193d87da323SSergio Paracuellos FIELD_PREP(RG_PE1_H_PLL_IR, 0x02) | 194d87da323SSergio Paracuellos FIELD_PREP(RG_PE1_H_PLL_IC, 0x01) | 195d87da323SSergio Paracuellos FIELD_PREP(RG_PE1_PLL_DIVEN, 0x02)); 196d87da323SSergio Paracuellos 197d87da323SSergio Paracuellos mt7621_phy_rmw(phy, RG_PE1_H_PLL_BR_REG, RG_PE1_H_PLL_BR, 198d87da323SSergio Paracuellos FIELD_PREP(RG_PE1_H_PLL_BR, 0x00)); 199d87da323SSergio Paracuellos 200*60ece833SSergio Paracuellos if (clk_rate == 40000000) { /* 40MHz Xtal */ 201d87da323SSergio Paracuellos /* set force mode enable of da_pe1_mstckdiv */ 202d87da323SSergio Paracuellos mt7621_phy_rmw(phy, RG_PE1_MSTCKDIV_REG, 203d87da323SSergio Paracuellos RG_PE1_MSTCKDIV | RG_PE1_FRC_MSTCKDIV, 204d87da323SSergio Paracuellos FIELD_PREP(RG_PE1_MSTCKDIV, 0x01) | 205d87da323SSergio Paracuellos RG_PE1_FRC_MSTCKDIV); 206d87da323SSergio Paracuellos } 207*60ece833SSergio Paracuellos 208*60ece833SSergio Paracuellos return 0; 209d87da323SSergio Paracuellos } 210d87da323SSergio Paracuellos 211d87da323SSergio Paracuellos static int mt7621_pci_phy_init(struct phy *phy) 212d87da323SSergio Paracuellos { 213d87da323SSergio Paracuellos struct mt7621_pci_phy *mphy = phy_get_drvdata(phy); 214d87da323SSergio Paracuellos 215d87da323SSergio Paracuellos if (mphy->bypass_pipe_rst) 216d87da323SSergio Paracuellos mt7621_bypass_pipe_rst(mphy); 217d87da323SSergio Paracuellos 218*60ece833SSergio Paracuellos return mt7621_set_phy_for_ssc(mphy); 219d87da323SSergio Paracuellos } 220d87da323SSergio Paracuellos 221d87da323SSergio Paracuellos static int mt7621_pci_phy_power_on(struct phy *phy) 222d87da323SSergio Paracuellos { 223d87da323SSergio Paracuellos struct mt7621_pci_phy *mphy = phy_get_drvdata(phy); 224d87da323SSergio Paracuellos 225d87da323SSergio Paracuellos /* Enable PHY and disable force mode */ 226d87da323SSergio Paracuellos mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG, 227d87da323SSergio Paracuellos RG_PE1_FRC_PHY_EN, RG_PE1_PHY_EN); 228d87da323SSergio Paracuellos 229d87da323SSergio Paracuellos if (mphy->has_dual_port) { 230d87da323SSergio Paracuellos mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH, 231d87da323SSergio Paracuellos RG_PE1_FRC_PHY_EN, RG_PE1_PHY_EN); 232d87da323SSergio Paracuellos } 233d87da323SSergio Paracuellos 234d87da323SSergio Paracuellos return 0; 235d87da323SSergio Paracuellos } 236d87da323SSergio Paracuellos 237d87da323SSergio Paracuellos static int mt7621_pci_phy_power_off(struct phy *phy) 238d87da323SSergio Paracuellos { 239d87da323SSergio Paracuellos struct mt7621_pci_phy *mphy = phy_get_drvdata(phy); 240d87da323SSergio Paracuellos 241d87da323SSergio Paracuellos /* Disable PHY */ 242d87da323SSergio Paracuellos mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG, 243d87da323SSergio Paracuellos RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN); 244d87da323SSergio Paracuellos 245d87da323SSergio Paracuellos if (mphy->has_dual_port) { 246d87da323SSergio Paracuellos mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH, 247d87da323SSergio Paracuellos RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN); 248d87da323SSergio Paracuellos } 249d87da323SSergio Paracuellos 250d87da323SSergio Paracuellos return 0; 251d87da323SSergio Paracuellos } 252d87da323SSergio Paracuellos 253d87da323SSergio Paracuellos static int mt7621_pci_phy_exit(struct phy *phy) 254d87da323SSergio Paracuellos { 255d87da323SSergio Paracuellos return 0; 256d87da323SSergio Paracuellos } 257d87da323SSergio Paracuellos 258d87da323SSergio Paracuellos static const struct phy_ops mt7621_pci_phy_ops = { 259d87da323SSergio Paracuellos .init = mt7621_pci_phy_init, 260d87da323SSergio Paracuellos .exit = mt7621_pci_phy_exit, 261d87da323SSergio Paracuellos .power_on = mt7621_pci_phy_power_on, 262d87da323SSergio Paracuellos .power_off = mt7621_pci_phy_power_off, 263d87da323SSergio Paracuellos .owner = THIS_MODULE, 264d87da323SSergio Paracuellos }; 265d87da323SSergio Paracuellos 266d87da323SSergio Paracuellos static struct phy *mt7621_pcie_phy_of_xlate(struct device *dev, 267d87da323SSergio Paracuellos struct of_phandle_args *args) 268d87da323SSergio Paracuellos { 269d87da323SSergio Paracuellos struct mt7621_pci_phy *mt7621_phy = dev_get_drvdata(dev); 270d87da323SSergio Paracuellos 271d87da323SSergio Paracuellos if (WARN_ON(args->args[0] >= MAX_PHYS)) 272d87da323SSergio Paracuellos return ERR_PTR(-ENODEV); 273d87da323SSergio Paracuellos 274d87da323SSergio Paracuellos mt7621_phy->has_dual_port = args->args[0]; 275d87da323SSergio Paracuellos 276d87da323SSergio Paracuellos dev_info(dev, "PHY for 0x%08x (dual port = %d)\n", 277d87da323SSergio Paracuellos (unsigned int)mt7621_phy->port_base, mt7621_phy->has_dual_port); 278d87da323SSergio Paracuellos 279d87da323SSergio Paracuellos return mt7621_phy->phy; 280d87da323SSergio Paracuellos } 281d87da323SSergio Paracuellos 282d87da323SSergio Paracuellos static const struct soc_device_attribute mt7621_pci_quirks_match[] = { 283d87da323SSergio Paracuellos { .soc_id = "mt7621", .revision = "E2" } 284d87da323SSergio Paracuellos }; 285d87da323SSergio Paracuellos 286d87da323SSergio Paracuellos static const struct regmap_config mt7621_pci_phy_regmap_config = { 287d87da323SSergio Paracuellos .reg_bits = 32, 288d87da323SSergio Paracuellos .val_bits = 32, 289d87da323SSergio Paracuellos .reg_stride = 4, 290d87da323SSergio Paracuellos .max_register = 0x700, 291d87da323SSergio Paracuellos }; 292d87da323SSergio Paracuellos 293d87da323SSergio Paracuellos static int mt7621_pci_phy_probe(struct platform_device *pdev) 294d87da323SSergio Paracuellos { 295d87da323SSergio Paracuellos struct device *dev = &pdev->dev; 296d87da323SSergio Paracuellos const struct soc_device_attribute *attr; 297d87da323SSergio Paracuellos struct phy_provider *provider; 298d87da323SSergio Paracuellos struct mt7621_pci_phy *phy; 299d87da323SSergio Paracuellos 300d87da323SSergio Paracuellos phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); 301d87da323SSergio Paracuellos if (!phy) 302d87da323SSergio Paracuellos return -ENOMEM; 303d87da323SSergio Paracuellos 304d87da323SSergio Paracuellos attr = soc_device_match(mt7621_pci_quirks_match); 305d87da323SSergio Paracuellos if (attr) 306d87da323SSergio Paracuellos phy->bypass_pipe_rst = true; 307d87da323SSergio Paracuellos 308d87da323SSergio Paracuellos phy->dev = dev; 309d87da323SSergio Paracuellos platform_set_drvdata(pdev, phy); 310d87da323SSergio Paracuellos 311d87da323SSergio Paracuellos phy->port_base = devm_platform_ioremap_resource(pdev, 0); 312d87da323SSergio Paracuellos if (IS_ERR(phy->port_base)) { 313d87da323SSergio Paracuellos dev_err(dev, "failed to remap phy regs\n"); 314d87da323SSergio Paracuellos return PTR_ERR(phy->port_base); 315d87da323SSergio Paracuellos } 316d87da323SSergio Paracuellos 317d87da323SSergio Paracuellos phy->regmap = devm_regmap_init_mmio(phy->dev, phy->port_base, 318d87da323SSergio Paracuellos &mt7621_pci_phy_regmap_config); 319d87da323SSergio Paracuellos if (IS_ERR(phy->regmap)) 320d87da323SSergio Paracuellos return PTR_ERR(phy->regmap); 321d87da323SSergio Paracuellos 322d87da323SSergio Paracuellos phy->phy = devm_phy_create(dev, dev->of_node, &mt7621_pci_phy_ops); 323b976c987SWei Yongjun if (IS_ERR(phy->phy)) { 324d87da323SSergio Paracuellos dev_err(dev, "failed to create phy\n"); 325b976c987SWei Yongjun return PTR_ERR(phy->phy); 326d87da323SSergio Paracuellos } 327d87da323SSergio Paracuellos 328*60ece833SSergio Paracuellos phy->sys_clk = devm_clk_get(dev, NULL); 329*60ece833SSergio Paracuellos if (IS_ERR(phy->sys_clk)) { 330*60ece833SSergio Paracuellos dev_err(dev, "failed to get phy clock\n"); 331*60ece833SSergio Paracuellos return PTR_ERR(phy->sys_clk); 332*60ece833SSergio Paracuellos } 333*60ece833SSergio Paracuellos 334d87da323SSergio Paracuellos phy_set_drvdata(phy->phy, phy); 335d87da323SSergio Paracuellos 336d87da323SSergio Paracuellos provider = devm_of_phy_provider_register(dev, mt7621_pcie_phy_of_xlate); 337d87da323SSergio Paracuellos 338d87da323SSergio Paracuellos return PTR_ERR_OR_ZERO(provider); 339d87da323SSergio Paracuellos } 340d87da323SSergio Paracuellos 341d87da323SSergio Paracuellos static const struct of_device_id mt7621_pci_phy_ids[] = { 342d87da323SSergio Paracuellos { .compatible = "mediatek,mt7621-pci-phy" }, 343d87da323SSergio Paracuellos {}, 344d87da323SSergio Paracuellos }; 3458145dcb0SSergio Paracuellos MODULE_DEVICE_TABLE(of, mt7621_pci_phy_ids); 346d87da323SSergio Paracuellos 347d87da323SSergio Paracuellos static struct platform_driver mt7621_pci_phy_driver = { 348d87da323SSergio Paracuellos .probe = mt7621_pci_phy_probe, 349d87da323SSergio Paracuellos .driver = { 350d87da323SSergio Paracuellos .name = "mt7621-pci-phy", 351d87da323SSergio Paracuellos .of_match_table = of_match_ptr(mt7621_pci_phy_ids), 352d87da323SSergio Paracuellos }, 353d87da323SSergio Paracuellos }; 354d87da323SSergio Paracuellos 355d87da323SSergio Paracuellos builtin_platform_driver(mt7621_pci_phy_driver); 356d87da323SSergio Paracuellos 357d87da323SSergio Paracuellos MODULE_AUTHOR("Sergio Paracuellos <sergio.paracuellos@gmail.com>"); 358d87da323SSergio Paracuellos MODULE_DESCRIPTION("MediaTek MT7621 PCIe PHY driver"); 359d87da323SSergio Paracuellos MODULE_LICENSE("GPL v2"); 360