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 struct resource *res; 133 void __iomem *base; 134 int ret; 135 136 priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL); 137 if (!priv) 138 return -ENOMEM; 139 140 priv->phy = devm_phy_create(dev, np, &phy_axg_pcie_ops); 141 if (IS_ERR(priv->phy)) { 142 ret = PTR_ERR(priv->phy); 143 if (ret != -EPROBE_DEFER) 144 dev_err(dev, "failed to create PHY\n"); 145 return ret; 146 } 147 148 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 149 base = devm_ioremap_resource(dev, res); 150 if (IS_ERR(base)) 151 return PTR_ERR(base); 152 153 priv->regmap = devm_regmap_init_mmio(dev, base, 154 &phy_axg_pcie_regmap_conf); 155 if (IS_ERR(priv->regmap)) 156 return PTR_ERR(priv->regmap); 157 158 priv->reset = devm_reset_control_array_get(dev, false, false); 159 if (IS_ERR(priv->reset)) 160 return PTR_ERR(priv->reset); 161 162 priv->analog = devm_phy_get(dev, "analog"); 163 if (IS_ERR(priv->analog)) 164 return PTR_ERR(priv->analog); 165 166 phy_set_drvdata(priv->phy, priv); 167 dev_set_drvdata(dev, priv); 168 pphy = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 169 170 return PTR_ERR_OR_ZERO(pphy); 171} 172 173static const struct of_device_id phy_axg_pcie_of_match[] = { 174 { 175 .compatible = "amlogic,axg-pcie-phy", 176 }, 177 { }, 178}; 179MODULE_DEVICE_TABLE(of, phy_axg_pcie_of_match); 180 181static struct platform_driver phy_axg_pcie_driver = { 182 .probe = phy_axg_pcie_probe, 183 .driver = { 184 .name = "phy-axg-pcie", 185 .of_match_table = phy_axg_pcie_of_match, 186 }, 187}; 188module_platform_driver(phy_axg_pcie_driver); 189 190MODULE_AUTHOR("Remi Pommarel <repk@triplefau.lt>"); 191MODULE_DESCRIPTION("Amlogic AXG PCIE PHY driver"); 192MODULE_LICENSE("GPL v2"); 193