1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Renesas R-Car Gen3 PCIe PHY driver 4 * 5 * Copyright (C) 2018 Cogent Embedded, Inc. 6 */ 7 8 #include <linux/clk.h> 9 #include <linux/io.h> 10 #include <linux/module.h> 11 #include <linux/of.h> 12 #include <linux/phy/phy.h> 13 #include <linux/platform_device.h> 14 #include <linux/spinlock.h> 15 16 #define PHY_CTRL 0x4000 /* R8A77980 only */ 17 18 /* PHY control register (PHY_CTRL) */ 19 #define PHY_CTRL_PHY_PWDN BIT(2) 20 21 struct rcar_gen3_phy { 22 struct phy *phy; 23 spinlock_t lock; 24 void __iomem *base; 25 }; 26 27 static void rcar_gen3_phy_pcie_modify_reg(struct phy *p, unsigned int reg, 28 u32 clear, u32 set) 29 { 30 struct rcar_gen3_phy *phy = phy_get_drvdata(p); 31 void __iomem *base = phy->base; 32 unsigned long flags; 33 u32 value; 34 35 spin_lock_irqsave(&phy->lock, flags); 36 37 value = readl(base + reg); 38 value &= ~clear; 39 value |= set; 40 writel(value, base + reg); 41 42 spin_unlock_irqrestore(&phy->lock, flags); 43 } 44 45 static int r8a77980_phy_pcie_power_on(struct phy *p) 46 { 47 /* Power on the PCIe PHY */ 48 rcar_gen3_phy_pcie_modify_reg(p, PHY_CTRL, PHY_CTRL_PHY_PWDN, 0); 49 50 return 0; 51 } 52 53 static int r8a77980_phy_pcie_power_off(struct phy *p) 54 { 55 /* Power off the PCIe PHY */ 56 rcar_gen3_phy_pcie_modify_reg(p, PHY_CTRL, 0, PHY_CTRL_PHY_PWDN); 57 58 return 0; 59 } 60 61 static const struct phy_ops r8a77980_phy_pcie_ops = { 62 .power_on = r8a77980_phy_pcie_power_on, 63 .power_off = r8a77980_phy_pcie_power_off, 64 .owner = THIS_MODULE, 65 }; 66 67 static const struct of_device_id rcar_gen3_phy_pcie_match_table[] = { 68 { .compatible = "renesas,r8a77980-pcie-phy" }, 69 { } 70 }; 71 MODULE_DEVICE_TABLE(of, rcar_gen3_phy_pcie_match_table); 72 73 static int rcar_gen3_phy_pcie_probe(struct platform_device *pdev) 74 { 75 struct device *dev = &pdev->dev; 76 struct phy_provider *provider; 77 struct rcar_gen3_phy *phy; 78 void __iomem *base; 79 int error; 80 81 if (!dev->of_node) { 82 dev_err(dev, 83 "This driver must only be instantiated from the device tree\n"); 84 return -EINVAL; 85 } 86 87 base = devm_platform_ioremap_resource(pdev, 0); 88 if (IS_ERR(base)) 89 return PTR_ERR(base); 90 91 phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); 92 if (!phy) 93 return -ENOMEM; 94 95 spin_lock_init(&phy->lock); 96 97 phy->base = base; 98 99 /* 100 * devm_phy_create() will call pm_runtime_enable(&phy->dev); 101 * And then, phy-core will manage runtime PM for this device. 102 */ 103 pm_runtime_enable(dev); 104 105 phy->phy = devm_phy_create(dev, NULL, &r8a77980_phy_pcie_ops); 106 if (IS_ERR(phy->phy)) { 107 dev_err(dev, "Failed to create PCIe PHY\n"); 108 error = PTR_ERR(phy->phy); 109 goto error; 110 } 111 phy_set_drvdata(phy->phy, phy); 112 113 provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 114 if (IS_ERR(provider)) { 115 dev_err(dev, "Failed to register PHY provider\n"); 116 error = PTR_ERR(provider); 117 goto error; 118 } 119 120 return 0; 121 122 error: 123 pm_runtime_disable(dev); 124 125 return error; 126 } 127 128 static void rcar_gen3_phy_pcie_remove(struct platform_device *pdev) 129 { 130 pm_runtime_disable(&pdev->dev); 131 }; 132 133 static struct platform_driver rcar_gen3_phy_driver = { 134 .driver = { 135 .name = "phy_rcar_gen3_pcie", 136 .of_match_table = rcar_gen3_phy_pcie_match_table, 137 }, 138 .probe = rcar_gen3_phy_pcie_probe, 139 .remove_new = rcar_gen3_phy_pcie_remove, 140 }; 141 142 module_platform_driver(rcar_gen3_phy_driver); 143 144 MODULE_LICENSE("GPL v2"); 145 MODULE_DESCRIPTION("Renesas R-Car Gen3 PCIe PHY"); 146 MODULE_AUTHOR("Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>"); 147