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 22struct rcar_gen3_phy { 23 struct phy *phy; 24 spinlock_t lock; 25 void __iomem *base; 26}; 27 28static 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 46static 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 54static 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 62static 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 68static const struct of_device_id rcar_gen3_phy_pcie_match_table[] = { 69 { .compatible = "renesas,r8a77980-pcie-phy" }, 70 { } 71}; 72MODULE_DEVICE_TABLE(of, rcar_gen3_phy_pcie_match_table); 73 74static 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 125error: 126 pm_runtime_disable(dev); 127 128 return error; 129} 130 131static int rcar_gen3_phy_pcie_remove(struct platform_device *pdev) 132{ 133 pm_runtime_disable(&pdev->dev); 134 135 return 0; 136}; 137 138static 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 147module_platform_driver(rcar_gen3_phy_driver); 148 149MODULE_LICENSE("GPL v2"); 150MODULE_DESCRIPTION("Renesas R-Car Gen3 PCIe PHY"); 151MODULE_AUTHOR("Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>"); 152