linux/drivers/phy/amlogic/phy-meson-axg-pcie.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Amlogic AXG PCIE PHY driver
   4 *
   5 * Copyright (C) 2020 Remi Pommarel <repk@triplefau.lt>
   6 */
   7#include <linux/module.h>
   8#include <linux/phy/phy.h>
   9#include <linux/regmap.h>
  10#include <linux/reset.h>
  11#include <linux/platform_device.h>
  12#include <linux/bitfield.h>
  13#include <dt-bindings/phy/phy.h>
  14
  15#define MESON_PCIE_REG0 0x00
  16#define         MESON_PCIE_COMMON_CLK   BIT(4)
  17#define         MESON_PCIE_PORT_SEL     GENMASK(3, 2)
  18#define         MESON_PCIE_CLK          BIT(1)
  19#define         MESON_PCIE_POWERDOWN    BIT(0)
  20
  21#define MESON_PCIE_TWO_X1               FIELD_PREP(MESON_PCIE_PORT_SEL, 0x3)
  22#define MESON_PCIE_COMMON_REF_CLK       FIELD_PREP(MESON_PCIE_COMMON_CLK, 0x1)
  23#define MESON_PCIE_PHY_INIT             (MESON_PCIE_TWO_X1 |            \
  24                                         MESON_PCIE_COMMON_REF_CLK)
  25#define MESON_PCIE_RESET_DELAY          500
  26
  27struct phy_axg_pcie_priv {
  28        struct phy *phy;
  29        struct phy *analog;
  30        struct regmap *regmap;
  31        struct reset_control *reset;
  32};
  33
  34static const struct regmap_config phy_axg_pcie_regmap_conf = {
  35        .reg_bits = 8,
  36        .val_bits = 32,
  37        .reg_stride = 4,
  38        .max_register = MESON_PCIE_REG0,
  39};
  40
  41static int phy_axg_pcie_power_on(struct phy *phy)
  42{
  43        struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy);
  44        int ret;
  45
  46        ret = phy_power_on(priv->analog);
  47        if (ret != 0)
  48                return ret;
  49
  50        regmap_update_bits(priv->regmap, MESON_PCIE_REG0,
  51                           MESON_PCIE_POWERDOWN, 0);
  52        return 0;
  53}
  54
  55static int phy_axg_pcie_power_off(struct phy *phy)
  56{
  57        struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy);
  58        int ret;
  59
  60        ret = phy_power_off(priv->analog);
  61        if (ret != 0)
  62                return ret;
  63
  64        regmap_update_bits(priv->regmap, MESON_PCIE_REG0,
  65                           MESON_PCIE_POWERDOWN, 1);
  66        return 0;
  67}
  68
  69static int phy_axg_pcie_init(struct phy *phy)
  70{
  71        struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy);
  72        int ret;
  73
  74        ret = phy_init(priv->analog);
  75        if (ret != 0)
  76                return ret;
  77
  78        regmap_write(priv->regmap, MESON_PCIE_REG0, MESON_PCIE_PHY_INIT);
  79        return reset_control_reset(priv->reset);
  80}
  81
  82static int phy_axg_pcie_exit(struct phy *phy)
  83{
  84        struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy);
  85        int ret;
  86
  87        ret = phy_exit(priv->analog);
  88        if (ret != 0)
  89                return ret;
  90
  91        return reset_control_reset(priv->reset);
  92}
  93
  94static int phy_axg_pcie_reset(struct phy *phy)
  95{
  96        struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy);
  97        int ret = 0;
  98
  99        ret = phy_reset(priv->analog);
 100        if (ret != 0)
 101                goto out;
 102
 103        ret = reset_control_assert(priv->reset);
 104        if (ret != 0)
 105                goto out;
 106        udelay(MESON_PCIE_RESET_DELAY);
 107
 108        ret = reset_control_deassert(priv->reset);
 109        if (ret != 0)
 110                goto out;
 111        udelay(MESON_PCIE_RESET_DELAY);
 112
 113out:
 114        return ret;
 115}
 116
 117static const struct phy_ops phy_axg_pcie_ops = {
 118        .init = phy_axg_pcie_init,
 119        .exit = phy_axg_pcie_exit,
 120        .power_on = phy_axg_pcie_power_on,
 121        .power_off = phy_axg_pcie_power_off,
 122        .reset = phy_axg_pcie_reset,
 123        .owner = THIS_MODULE,
 124};
 125
 126static int phy_axg_pcie_probe(struct platform_device *pdev)
 127{
 128        struct phy_provider *pphy;
 129        struct device *dev = &pdev->dev;
 130        struct phy_axg_pcie_priv *priv;
 131        struct device_node *np = dev->of_node;
 132        void __iomem *base;
 133        int ret;
 134
 135        priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL);
 136        if (!priv)
 137                return -ENOMEM;
 138
 139        priv->phy = devm_phy_create(dev, np, &phy_axg_pcie_ops);
 140        if (IS_ERR(priv->phy)) {
 141                ret = PTR_ERR(priv->phy);
 142                if (ret != -EPROBE_DEFER)
 143                        dev_err(dev, "failed to create PHY\n");
 144                return ret;
 145        }
 146
 147        base = devm_platform_ioremap_resource(pdev, 0);
 148        if (IS_ERR(base))
 149                return PTR_ERR(base);
 150
 151        priv->regmap = devm_regmap_init_mmio(dev, base,
 152                                             &phy_axg_pcie_regmap_conf);
 153        if (IS_ERR(priv->regmap))
 154                return PTR_ERR(priv->regmap);
 155
 156        priv->reset = devm_reset_control_array_get_exclusive(dev);
 157        if (IS_ERR(priv->reset))
 158                return PTR_ERR(priv->reset);
 159
 160        priv->analog = devm_phy_get(dev, "analog");
 161        if (IS_ERR(priv->analog))
 162                return PTR_ERR(priv->analog);
 163
 164        phy_set_drvdata(priv->phy, priv);
 165        dev_set_drvdata(dev, priv);
 166        pphy = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 167
 168        return PTR_ERR_OR_ZERO(pphy);
 169}
 170
 171static const struct of_device_id phy_axg_pcie_of_match[] = {
 172        {
 173                .compatible = "amlogic,axg-pcie-phy",
 174        },
 175        { },
 176};
 177MODULE_DEVICE_TABLE(of, phy_axg_pcie_of_match);
 178
 179static struct platform_driver phy_axg_pcie_driver = {
 180        .probe = phy_axg_pcie_probe,
 181        .driver = {
 182                .name = "phy-axg-pcie",
 183                .of_match_table = phy_axg_pcie_of_match,
 184        },
 185};
 186module_platform_driver(phy_axg_pcie_driver);
 187
 188MODULE_AUTHOR("Remi Pommarel <repk@triplefau.lt>");
 189MODULE_DESCRIPTION("Amlogic AXG PCIE PHY driver");
 190MODULE_LICENSE("GPL v2");
 191