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 void __iomem *base; 80 int error; 81 82 if (!dev->of_node) { 83 dev_err(dev, 84 "This driver must only be instantiated from the device tree\n"); 85 return -EINVAL; 86 } 87 88 base = devm_platform_ioremap_resource(pdev, 0); 89 if (IS_ERR(base)) 90 return PTR_ERR(base); 91 92 phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); 93 if (!phy) 94 return -ENOMEM; 95 96 spin_lock_init(&phy->lock); 97 98 phy->base = base; 99 100 /* 101 * devm_phy_create() will call pm_runtime_enable(&phy->dev); 102 * And then, phy-core will manage runtime PM for this device. 103 */ 104 pm_runtime_enable(dev); 105 106 phy->phy = devm_phy_create(dev, NULL, &r8a77980_phy_pcie_ops); 107 if (IS_ERR(phy->phy)) { 108 dev_err(dev, "Failed to create PCIe PHY\n"); 109 error = PTR_ERR(phy->phy); 110 goto error; 111 } 112 phy_set_drvdata(phy->phy, phy); 113 114 provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 115 if (IS_ERR(provider)) { 116 dev_err(dev, "Failed to register PHY provider\n"); 117 error = PTR_ERR(provider); 118 goto error; 119 } 120 121 return 0; 122 123 error: 124 pm_runtime_disable(dev); 125 126 return error; 127 } 128 129 static int rcar_gen3_phy_pcie_remove(struct platform_device *pdev) 130 { 131 pm_runtime_disable(&pdev->dev); 132 133 return 0; 134 }; 135 136 static struct platform_driver rcar_gen3_phy_driver = { 137 .driver = { 138 .name = "phy_rcar_gen3_pcie", 139 .of_match_table = rcar_gen3_phy_pcie_match_table, 140 }, 141 .probe = rcar_gen3_phy_pcie_probe, 142 .remove = rcar_gen3_phy_pcie_remove, 143 }; 144 145 module_platform_driver(rcar_gen3_phy_driver); 146 147 MODULE_LICENSE("GPL v2"); 148 MODULE_DESCRIPTION("Renesas R-Car Gen3 PCIe PHY"); 149 MODULE_AUTHOR("Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>"); 150