linux/drivers/phy/socionext/phy-uniphier-pcie.c
Rob Herring 7559e7572c phy: Explicitly include correct DT includes
The DT of_device.h and of_platform.h date back to the separate
of_platform_bus_type before it as merged into the regular platform bus.
As part of that merge prepping Arm DT support 13 years ago, they
"temporarily" include each other. They also include platform_device.h
and of.h. As a result, there's a pretty much random mix of those include
files used throughout the tree. In order to detangle these headers and
replace the implicit includes with struct declarations, users need to
explicitly include the correct includes.

Signed-off-by: Rob Herring <robh@kernel.org>
Acked-by: Marc Kleine-Budde <mkl@pengutronix.de> # for drivers/phy/phy-can-transceiver.c
Acked-by: Heiko Stuebner <heiko@sntech.de>
Acked-by: Sergio Paracuellos <sergio.paracuellos@gmail.com>
Link: https://lore.kernel.org/r/20230714174841.4061919-1-robh@kernel.org
Signed-off-by: Vinod Koul <vkoul@kernel.org>
2023-07-17 11:52:56 +05:30

350 lines
8.8 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* phy-uniphier-pcie.c - PHY driver for UniPhier PCIe controller
* Copyright 2018, Socionext Inc.
* Author: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
*/
#include <linux/bitops.h>
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/iopoll.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/resource.h>
/* PHY */
#define PCL_PHY_CLKCTRL 0x0000
#define PORT_SEL_MASK GENMASK(11, 9)
#define PORT_SEL_1 FIELD_PREP(PORT_SEL_MASK, 1)
#define PCL_PHY_TEST_I 0x2000
#define TESTI_DAT_MASK GENMASK(13, 6)
#define TESTI_ADR_MASK GENMASK(5, 1)
#define TESTI_WR_EN BIT(0)
#define TESTIO_PHY_SHIFT 16
#define PCL_PHY_TEST_O 0x2004
#define TESTO_DAT_MASK GENMASK(7, 0)
#define PCL_PHY_RESET 0x200c
#define PCL_PHY_RESET_N_MNMODE BIT(8) /* =1:manual */
#define PCL_PHY_RESET_N BIT(0) /* =1:deasssert */
/* SG */
#define SG_USBPCIESEL 0x590
#define SG_USBPCIESEL_PCIE BIT(0)
/* SC */
#define SC_US3SRCSEL 0x2244
#define SC_US3SRCSEL_2LANE GENMASK(9, 8)
#define PCL_PHY_R00 0
#define RX_EQ_ADJ_EN BIT(3) /* enable for EQ adjustment */
#define PCL_PHY_R06 6
#define RX_EQ_ADJ GENMASK(5, 0) /* EQ adjustment value */
#define RX_EQ_ADJ_VAL 0
#define PCL_PHY_R26 26
#define VCO_CTRL GENMASK(7, 4) /* Tx VCO adjustment value */
#define VCO_CTRL_INIT_VAL 5
#define PCL_PHY_R28 28
#define VCOPLL_CLMP GENMASK(3, 2) /* Tx VCOPLL clamp mode */
#define VCOPLL_CLMP_VAL 0
struct uniphier_pciephy_priv {
void __iomem *base;
struct device *dev;
struct clk *clk, *clk_gio;
struct reset_control *rst, *rst_gio;
const struct uniphier_pciephy_soc_data *data;
};
struct uniphier_pciephy_soc_data {
bool is_legacy;
bool is_dual_phy;
void (*set_phymode)(struct regmap *regmap);
};
static void uniphier_pciephy_testio_write(struct uniphier_pciephy_priv *priv,
int id, u32 data)
{
if (id)
data <<= TESTIO_PHY_SHIFT;
/* need to read TESTO twice after accessing TESTI */
writel(data, priv->base + PCL_PHY_TEST_I);
readl(priv->base + PCL_PHY_TEST_O);
readl(priv->base + PCL_PHY_TEST_O);
}
static u32 uniphier_pciephy_testio_read(struct uniphier_pciephy_priv *priv, int id)
{
u32 val = readl(priv->base + PCL_PHY_TEST_O);
if (id)
val >>= TESTIO_PHY_SHIFT;
return val & TESTO_DAT_MASK;
}
static void uniphier_pciephy_set_param(struct uniphier_pciephy_priv *priv,
int id, u32 reg, u32 mask, u32 param)
{
u32 val;
/* read previous data */
val = FIELD_PREP(TESTI_DAT_MASK, 1);
val |= FIELD_PREP(TESTI_ADR_MASK, reg);
uniphier_pciephy_testio_write(priv, id, val);
val = uniphier_pciephy_testio_read(priv, id);
/* update value */
val &= ~mask;
val |= mask & param;
val = FIELD_PREP(TESTI_DAT_MASK, val);
val |= FIELD_PREP(TESTI_ADR_MASK, reg);
uniphier_pciephy_testio_write(priv, id, val);
uniphier_pciephy_testio_write(priv, id, val | TESTI_WR_EN);
uniphier_pciephy_testio_write(priv, id, val);
/* read current data as dummy */
val = FIELD_PREP(TESTI_DAT_MASK, 1);
val |= FIELD_PREP(TESTI_ADR_MASK, reg);
uniphier_pciephy_testio_write(priv, id, val);
uniphier_pciephy_testio_read(priv, id);
}
static void uniphier_pciephy_assert(struct uniphier_pciephy_priv *priv)
{
u32 val;
val = readl(priv->base + PCL_PHY_RESET);
val &= ~PCL_PHY_RESET_N;
val |= PCL_PHY_RESET_N_MNMODE;
writel(val, priv->base + PCL_PHY_RESET);
}
static void uniphier_pciephy_deassert(struct uniphier_pciephy_priv *priv)
{
u32 val;
val = readl(priv->base + PCL_PHY_RESET);
val |= PCL_PHY_RESET_N_MNMODE | PCL_PHY_RESET_N;
writel(val, priv->base + PCL_PHY_RESET);
}
static int uniphier_pciephy_init(struct phy *phy)
{
struct uniphier_pciephy_priv *priv = phy_get_drvdata(phy);
u32 val;
int ret, id;
ret = clk_prepare_enable(priv->clk);
if (ret)
return ret;
ret = clk_prepare_enable(priv->clk_gio);
if (ret)
goto out_clk_disable;
ret = reset_control_deassert(priv->rst);
if (ret)
goto out_clk_gio_disable;
ret = reset_control_deassert(priv->rst_gio);
if (ret)
goto out_rst_assert;
/* support only 1 port */
val = readl(priv->base + PCL_PHY_CLKCTRL);
val &= ~PORT_SEL_MASK;
val |= PORT_SEL_1;
writel(val, priv->base + PCL_PHY_CLKCTRL);
/* legacy controller doesn't have phy_reset and parameters */
if (priv->data->is_legacy)
return 0;
for (id = 0; id < (priv->data->is_dual_phy ? 2 : 1); id++) {
uniphier_pciephy_set_param(priv, id, PCL_PHY_R00,
RX_EQ_ADJ_EN, RX_EQ_ADJ_EN);
uniphier_pciephy_set_param(priv, id, PCL_PHY_R06, RX_EQ_ADJ,
FIELD_PREP(RX_EQ_ADJ, RX_EQ_ADJ_VAL));
uniphier_pciephy_set_param(priv, id, PCL_PHY_R26, VCO_CTRL,
FIELD_PREP(VCO_CTRL, VCO_CTRL_INIT_VAL));
uniphier_pciephy_set_param(priv, id, PCL_PHY_R28, VCOPLL_CLMP,
FIELD_PREP(VCOPLL_CLMP, VCOPLL_CLMP_VAL));
}
usleep_range(1, 10);
uniphier_pciephy_deassert(priv);
usleep_range(1, 10);
return 0;
out_rst_assert:
reset_control_assert(priv->rst);
out_clk_gio_disable:
clk_disable_unprepare(priv->clk_gio);
out_clk_disable:
clk_disable_unprepare(priv->clk);
return ret;
}
static int uniphier_pciephy_exit(struct phy *phy)
{
struct uniphier_pciephy_priv *priv = phy_get_drvdata(phy);
if (!priv->data->is_legacy)
uniphier_pciephy_assert(priv);
reset_control_assert(priv->rst_gio);
reset_control_assert(priv->rst);
clk_disable_unprepare(priv->clk_gio);
clk_disable_unprepare(priv->clk);
return 0;
}
static const struct phy_ops uniphier_pciephy_ops = {
.init = uniphier_pciephy_init,
.exit = uniphier_pciephy_exit,
.owner = THIS_MODULE,
};
static int uniphier_pciephy_probe(struct platform_device *pdev)
{
struct uniphier_pciephy_priv *priv;
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
struct regmap *regmap;
struct phy *phy;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->data = of_device_get_match_data(dev);
if (WARN_ON(!priv->data))
return -EINVAL;
priv->dev = dev;
priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
if (priv->data->is_legacy) {
priv->clk_gio = devm_clk_get(dev, "gio");
if (IS_ERR(priv->clk_gio))
return PTR_ERR(priv->clk_gio);
priv->rst_gio =
devm_reset_control_get_shared(dev, "gio");
if (IS_ERR(priv->rst_gio))
return PTR_ERR(priv->rst_gio);
priv->clk = devm_clk_get(dev, "link");
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
priv->rst = devm_reset_control_get_shared(dev, "link");
if (IS_ERR(priv->rst))
return PTR_ERR(priv->rst);
} else {
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
priv->rst = devm_reset_control_get_shared(dev, NULL);
if (IS_ERR(priv->rst))
return PTR_ERR(priv->rst);
}
phy = devm_phy_create(dev, dev->of_node, &uniphier_pciephy_ops);
if (IS_ERR(phy))
return PTR_ERR(phy);
regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
"socionext,syscon");
if (!IS_ERR(regmap) && priv->data->set_phymode)
priv->data->set_phymode(regmap);
phy_set_drvdata(phy, priv);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static void uniphier_pciephy_ld20_setmode(struct regmap *regmap)
{
regmap_update_bits(regmap, SG_USBPCIESEL,
SG_USBPCIESEL_PCIE, SG_USBPCIESEL_PCIE);
}
static void uniphier_pciephy_nx1_setmode(struct regmap *regmap)
{
regmap_update_bits(regmap, SC_US3SRCSEL,
SC_US3SRCSEL_2LANE, SC_US3SRCSEL_2LANE);
}
static const struct uniphier_pciephy_soc_data uniphier_pro5_data = {
.is_legacy = true,
};
static const struct uniphier_pciephy_soc_data uniphier_ld20_data = {
.is_legacy = false,
.is_dual_phy = false,
.set_phymode = uniphier_pciephy_ld20_setmode,
};
static const struct uniphier_pciephy_soc_data uniphier_pxs3_data = {
.is_legacy = false,
.is_dual_phy = false,
};
static const struct uniphier_pciephy_soc_data uniphier_nx1_data = {
.is_legacy = false,
.is_dual_phy = true,
.set_phymode = uniphier_pciephy_nx1_setmode,
};
static const struct of_device_id uniphier_pciephy_match[] = {
{
.compatible = "socionext,uniphier-pro5-pcie-phy",
.data = &uniphier_pro5_data,
},
{
.compatible = "socionext,uniphier-ld20-pcie-phy",
.data = &uniphier_ld20_data,
},
{
.compatible = "socionext,uniphier-pxs3-pcie-phy",
.data = &uniphier_pxs3_data,
},
{
.compatible = "socionext,uniphier-nx1-pcie-phy",
.data = &uniphier_nx1_data,
},
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, uniphier_pciephy_match);
static struct platform_driver uniphier_pciephy_driver = {
.probe = uniphier_pciephy_probe,
.driver = {
.name = "uniphier-pcie-phy",
.of_match_table = uniphier_pciephy_match,
},
};
module_platform_driver(uniphier_pciephy_driver);
MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");
MODULE_DESCRIPTION("UniPhier PHY driver for PCIe controller");
MODULE_LICENSE("GPL v2");