Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

phy: ralink: Add PHY driver for MT7621 PCIe PHY

This patch adds a driver for the PCIe PHY of MT7621 SoC.

Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com>
Link: https://lore.kernel.org/r/20201121155037.21354-3-sergio.paracuellos@gmail.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Sergio Paracuellos and committed by
Vinod Koul
d87da323 27076a73

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