Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Renesas R-Car Gen3 PCIe PHY driver
0004  *
0005  * Copyright (C) 2018 Cogent Embedded, Inc.
0006  */
0007 
0008 #include <linux/clk.h>
0009 #include <linux/io.h>
0010 #include <linux/module.h>
0011 #include <linux/of.h>
0012 #include <linux/phy/phy.h>
0013 #include <linux/of_device.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/spinlock.h>
0016 
0017 #define PHY_CTRL        0x4000      /* R8A77980 only */
0018 
0019 /* PHY control register (PHY_CTRL) */
0020 #define PHY_CTRL_PHY_PWDN   BIT(2)
0021 
0022 struct rcar_gen3_phy {
0023     struct phy *phy;
0024     spinlock_t lock;
0025     void __iomem *base;
0026 };
0027 
0028 static void rcar_gen3_phy_pcie_modify_reg(struct phy *p, unsigned int reg,
0029                       u32 clear, u32 set)
0030 {
0031     struct rcar_gen3_phy *phy = phy_get_drvdata(p);
0032     void __iomem *base = phy->base;
0033     unsigned long flags;
0034     u32 value;
0035 
0036     spin_lock_irqsave(&phy->lock, flags);
0037 
0038     value = readl(base + reg);
0039     value &= ~clear;
0040     value |= set;
0041     writel(value, base + reg);
0042 
0043     spin_unlock_irqrestore(&phy->lock, flags);
0044 }
0045 
0046 static int r8a77980_phy_pcie_power_on(struct phy *p)
0047 {
0048     /* Power on the PCIe PHY */
0049     rcar_gen3_phy_pcie_modify_reg(p, PHY_CTRL, PHY_CTRL_PHY_PWDN, 0);
0050 
0051     return 0;
0052 }
0053 
0054 static int r8a77980_phy_pcie_power_off(struct phy *p)
0055 {
0056     /* Power off the PCIe PHY */
0057     rcar_gen3_phy_pcie_modify_reg(p, PHY_CTRL, 0, PHY_CTRL_PHY_PWDN);
0058 
0059     return 0;
0060 }
0061 
0062 static const struct phy_ops r8a77980_phy_pcie_ops = {
0063     .power_on   = r8a77980_phy_pcie_power_on,
0064     .power_off  = r8a77980_phy_pcie_power_off,
0065     .owner      = THIS_MODULE,
0066 };
0067 
0068 static const struct of_device_id rcar_gen3_phy_pcie_match_table[] = {
0069     { .compatible = "renesas,r8a77980-pcie-phy" },
0070     { }
0071 };
0072 MODULE_DEVICE_TABLE(of, rcar_gen3_phy_pcie_match_table);
0073 
0074 static int rcar_gen3_phy_pcie_probe(struct platform_device *pdev)
0075 {
0076     struct device *dev = &pdev->dev;
0077     struct phy_provider *provider;
0078     struct rcar_gen3_phy *phy;
0079     void __iomem *base;
0080     int error;
0081 
0082     if (!dev->of_node) {
0083         dev_err(dev,
0084             "This driver must only be instantiated from the device tree\n");
0085         return -EINVAL;
0086     }
0087 
0088     base = devm_platform_ioremap_resource(pdev, 0);
0089     if (IS_ERR(base))
0090         return PTR_ERR(base);
0091 
0092     phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
0093     if (!phy)
0094         return -ENOMEM;
0095 
0096     spin_lock_init(&phy->lock);
0097 
0098     phy->base = base;
0099 
0100     /*
0101      * devm_phy_create() will call pm_runtime_enable(&phy->dev);
0102      * And then, phy-core will manage runtime PM for this device.
0103      */
0104     pm_runtime_enable(dev);
0105 
0106     phy->phy = devm_phy_create(dev, NULL, &r8a77980_phy_pcie_ops);
0107     if (IS_ERR(phy->phy)) {
0108         dev_err(dev, "Failed to create PCIe PHY\n");
0109         error = PTR_ERR(phy->phy);
0110         goto error;
0111     }
0112     phy_set_drvdata(phy->phy, phy);
0113 
0114     provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
0115     if (IS_ERR(provider)) {
0116         dev_err(dev, "Failed to register PHY provider\n");
0117         error = PTR_ERR(provider);
0118         goto error;
0119     }
0120 
0121     return 0;
0122 
0123 error:
0124     pm_runtime_disable(dev);
0125 
0126     return error;
0127 }
0128 
0129 static int rcar_gen3_phy_pcie_remove(struct platform_device *pdev)
0130 {
0131     pm_runtime_disable(&pdev->dev);
0132 
0133     return 0;
0134 };
0135 
0136 static struct platform_driver rcar_gen3_phy_driver = {
0137     .driver = {
0138         .name       = "phy_rcar_gen3_pcie",
0139         .of_match_table = rcar_gen3_phy_pcie_match_table,
0140     },
0141     .probe  = rcar_gen3_phy_pcie_probe,
0142     .remove = rcar_gen3_phy_pcie_remove,
0143 };
0144 
0145 module_platform_driver(rcar_gen3_phy_driver);
0146 
0147 MODULE_LICENSE("GPL v2");
0148 MODULE_DESCRIPTION("Renesas R-Car Gen3 PCIe PHY");
0149 MODULE_AUTHOR("Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>");