linux/drivers/phy/renesas/phy-rcar-gen3-pcie.c
<<
>>
Prefs
   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        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
 123error:
 124        pm_runtime_disable(dev);
 125
 126        return error;
 127}
 128
 129static int rcar_gen3_phy_pcie_remove(struct platform_device *pdev)
 130{
 131        pm_runtime_disable(&pdev->dev);
 132
 133        return 0;
 134};
 135
 136static 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
 145module_platform_driver(rcar_gen3_phy_driver);
 146
 147MODULE_LICENSE("GPL v2");
 148MODULE_DESCRIPTION("Renesas R-Car Gen3 PCIe PHY");
 149MODULE_AUTHOR("Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>");
 150