linux/drivers/phy/amlogic/phy-meson-axg-pcie.c
Anand Moon bdeff6d8a2 phy: amlogic: phy-meson-axg-pcie: Fix PHY creation order in axg-pcie probe
Reorder the PHY creation in the axg-pcie probe function to ensure all
the resource is mapped before creating the PHY. This change addresses
the issue where the PHY creation was attempted before
mapping the necessary resources, potentially causing failures.

Signed-off-by: Anand Moon <linux.amoon@gmail.com>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Link: https://lore.kernel.org/r/20250410133332.294556-7-linux.amoon@gmail.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
2025-04-11 17:07:15 +05:30

187 lines
4.3 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Amlogic AXG PCIE PHY driver
*
* Copyright (C) 2020 Remi Pommarel <repk@triplefau.lt>
*/
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/phy/phy.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/platform_device.h>
#include <linux/bitfield.h>
#include <dt-bindings/phy/phy.h>
#define MESON_PCIE_REG0 0x00
#define MESON_PCIE_COMMON_CLK BIT(4)
#define MESON_PCIE_PORT_SEL GENMASK(3, 2)
#define MESON_PCIE_CLK BIT(1)
#define MESON_PCIE_POWERDOWN BIT(0)
#define MESON_PCIE_TWO_X1 FIELD_PREP(MESON_PCIE_PORT_SEL, 0x3)
#define MESON_PCIE_COMMON_REF_CLK FIELD_PREP(MESON_PCIE_COMMON_CLK, 0x1)
#define MESON_PCIE_PHY_INIT (MESON_PCIE_TWO_X1 | \
MESON_PCIE_COMMON_REF_CLK)
#define MESON_PCIE_RESET_DELAY 500
struct phy_axg_pcie_priv {
struct phy *phy;
struct phy *analog;
struct regmap *regmap;
struct reset_control *reset;
};
static const struct regmap_config phy_axg_pcie_regmap_conf = {
.reg_bits = 8,
.val_bits = 32,
.reg_stride = 4,
.max_register = MESON_PCIE_REG0,
};
static int phy_axg_pcie_power_on(struct phy *phy)
{
struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy);
int ret;
ret = phy_power_on(priv->analog);
if (ret != 0)
return ret;
regmap_update_bits(priv->regmap, MESON_PCIE_REG0,
MESON_PCIE_POWERDOWN, 0);
return 0;
}
static int phy_axg_pcie_power_off(struct phy *phy)
{
struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy);
int ret;
ret = phy_power_off(priv->analog);
if (ret != 0)
return ret;
regmap_update_bits(priv->regmap, MESON_PCIE_REG0,
MESON_PCIE_POWERDOWN, 1);
return 0;
}
static int phy_axg_pcie_init(struct phy *phy)
{
struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy);
int ret;
ret = phy_init(priv->analog);
if (ret != 0)
return ret;
regmap_write(priv->regmap, MESON_PCIE_REG0, MESON_PCIE_PHY_INIT);
return reset_control_reset(priv->reset);
}
static int phy_axg_pcie_exit(struct phy *phy)
{
struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy);
int ret;
ret = phy_exit(priv->analog);
if (ret != 0)
return ret;
return reset_control_reset(priv->reset);
}
static int phy_axg_pcie_reset(struct phy *phy)
{
struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy);
int ret = 0;
ret = phy_reset(priv->analog);
if (ret != 0)
goto out;
ret = reset_control_assert(priv->reset);
if (ret != 0)
goto out;
udelay(MESON_PCIE_RESET_DELAY);
ret = reset_control_deassert(priv->reset);
if (ret != 0)
goto out;
udelay(MESON_PCIE_RESET_DELAY);
out:
return ret;
}
static const struct phy_ops phy_axg_pcie_ops = {
.init = phy_axg_pcie_init,
.exit = phy_axg_pcie_exit,
.power_on = phy_axg_pcie_power_on,
.power_off = phy_axg_pcie_power_off,
.reset = phy_axg_pcie_reset,
.owner = THIS_MODULE,
};
static int phy_axg_pcie_probe(struct platform_device *pdev)
{
struct phy_provider *pphy;
struct device *dev = &pdev->dev;
struct phy_axg_pcie_priv *priv;
struct device_node *np = dev->of_node;
void __iomem *base;
priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
priv->regmap = devm_regmap_init_mmio(dev, base,
&phy_axg_pcie_regmap_conf);
if (IS_ERR(priv->regmap))
return PTR_ERR(priv->regmap);
priv->reset = devm_reset_control_array_get_exclusive(dev);
if (IS_ERR(priv->reset))
return PTR_ERR(priv->reset);
priv->analog = devm_phy_get(dev, "analog");
if (IS_ERR(priv->analog))
return PTR_ERR(priv->analog);
priv->phy = devm_phy_create(dev, np, &phy_axg_pcie_ops);
if (IS_ERR(priv->phy))
return dev_err_probe(dev, PTR_ERR(priv->phy),
"failed to create PHY\n");
phy_set_drvdata(priv->phy, priv);
dev_set_drvdata(dev, priv);
pphy = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(pphy);
}
static const struct of_device_id phy_axg_pcie_of_match[] = {
{
.compatible = "amlogic,axg-pcie-phy",
},
{ },
};
MODULE_DEVICE_TABLE(of, phy_axg_pcie_of_match);
static struct platform_driver phy_axg_pcie_driver = {
.probe = phy_axg_pcie_probe,
.driver = {
.name = "phy-axg-pcie",
.of_match_table = phy_axg_pcie_of_match,
},
};
module_platform_driver(phy_axg_pcie_driver);
MODULE_AUTHOR("Remi Pommarel <repk@triplefau.lt>");
MODULE_DESCRIPTION("Amlogic AXG PCIE PHY driver");
MODULE_LICENSE("GPL v2");