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