diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig index e812adad7242..4d97aac8443a 100644 --- a/drivers/phy/rockchip/Kconfig +++ b/drivers/phy/rockchip/Kconfig @@ -75,6 +75,15 @@ config PHY_ROCKCHIP_PCIE help Enable this to support the Rockchip PCIe PHY. +config PHY_ROCKCHIP_SNPS_PCIE3 + tristate "Rockchip Snps PCIe3 PHY Driver" + depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST + depends on HAS_IOMEM + select GENERIC_PHY + select MFD_SYSCON + help + Enable this to support the Rockchip snps PCIe3 PHY. + config PHY_ROCKCHIP_TYPEC tristate "Rockchip TYPEC PHY Driver" depends on OF && (ARCH_ROCKCHIP || COMPILE_TEST) diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile index f0eec212b2aa..e15f41edf348 100644 --- a/drivers/phy/rockchip/Makefile +++ b/drivers/phy/rockchip/Makefile @@ -7,5 +7,6 @@ obj-$(CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY) += phy-rockchip-inno-dsidphy.o obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o +obj-$(CONFIG_PHY_ROCKCHIP_SNPS_PCIE3) += phy-rockchip-snps-pcie3.o obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o diff --git a/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c new file mode 100644 index 000000000000..7dd320c24b1b --- /dev/null +++ b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip PCIE3.0 phy driver + * + * Copyright (C) 2020 Rockchip Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GRF_PCIE30PHY_CON1 0x4 +#define GRF_PCIE30PHY_CON6 0x18 +#define GRF_PCIE30PHY_CON9 0x24 +#define GRF_PCIE30PHY_STATUS0 0x80 +#define SRAM_INIT_DONE(reg) (reg & BIT(14)) + +struct rockchip_p3phy_priv { + void __iomem *mmio; + int mode; + struct regmap *phy_grf; + struct reset_control *p30phy; + struct clk *ref_clk_m; + struct clk *ref_clk_n; + struct clk *pclk; + struct phy *phy; + bool is_bifurcation; +}; + +static int rockchip_p3phy_set_mode(struct phy *phy, enum phy_mode mode, int submode) +{ + struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy); + + /* Acutally We don't care EP/RC mode, but just record it */ + switch (mode) { + case PHY_MODE_PCIE_RC: + priv->mode = PHY_MODE_PCIE_RC; + break; + case PHY_MODE_PCIE_EP: + priv->mode = PHY_MODE_PCIE_EP; + break; + case PHY_MODE_PCIE_BIFURCATION: + priv->is_bifurcation = true; + break; + default: + pr_info("%s, invalid mode\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int rochchip_p3phy_init(struct phy *phy) +{ + struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy); + int ret; + u32 reg; + + ret = clk_prepare_enable(priv->ref_clk_m); + if (ret < 0) + return ret; + + ret = clk_prepare_enable(priv->ref_clk_n); + if (ret < 0) + goto err_ref; + + ret = clk_prepare_enable(priv->pclk); + if (ret < 0) + goto err_pclk; + + reset_control_assert(priv->p30phy); + udelay(1); + + /* Deassert PCIe PMA output clamp mode */ + regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON9, + (0x1 << 15) | (0x1 << 31)); + /* Set bifurcation if needed, and it doesn't care RC/EP */ + if (priv->is_bifurcation) { + regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON6, + 0x1 | (0xf << 16)); + regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON1, + (0x1 << 15) | (0x1 << 31)); + } + + reset_control_deassert(priv->p30phy); + + ret = regmap_read_poll_timeout(priv->phy_grf, + GRF_PCIE30PHY_STATUS0, + reg, SRAM_INIT_DONE(reg), + 0, 500); + if (ret) { + pr_err("%s: lock failed 0x%x, check input refclk and power supply\n", + __func__, reg); + goto err_pclk; + } + + return 0; +err_pclk: + clk_disable_unprepare(priv->ref_clk_n); +err_ref: + clk_disable_unprepare(priv->ref_clk_m); + return ret; +} + +static int rochchip_p3phy_exit(struct phy *phy) +{ + struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy); + clk_disable_unprepare(priv->ref_clk_m); + clk_disable_unprepare(priv->ref_clk_n); + clk_disable_unprepare(priv->pclk); + reset_control_assert(priv->p30phy); + return 0; +} + +static const struct phy_ops rochchip_p3phy_ops = { + .init = rochchip_p3phy_init, + .exit = rochchip_p3phy_exit, + .set_mode = rockchip_p3phy_set_mode, + .owner = THIS_MODULE, +}; + +static int rockchip_p3phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct rockchip_p3phy_priv *priv; + struct device_node *np = dev->of_node; + struct resource *res; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->mmio = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->mmio)) { + ret = PTR_ERR(priv->mmio); + return ret; + } + + priv->phy_grf = syscon_regmap_lookup_by_phandle(np, "rockchip,phy-grf"); + if (IS_ERR(priv->phy_grf)) { + dev_err(dev, "failed to find rockchip,phy_grf regmap\n"); + return PTR_ERR(priv->phy_grf); + } + + priv->phy = devm_phy_create(dev, NULL, &rochchip_p3phy_ops); + if (IS_ERR(priv->phy)) { + dev_err(dev, "failed to create combphy\n"); + return PTR_ERR(priv->phy); + } + + priv->p30phy = devm_reset_control_get(dev, "phy"); + if (IS_ERR(priv->p30phy)) { + dev_warn(dev, "no phy reset control specified\n"); + priv->p30phy = NULL; + } + + priv->ref_clk_m = devm_clk_get(dev, "refclk_m"); + if (IS_ERR(priv->ref_clk_m)) { + dev_err(dev, "failed to find ref clock M\n"); + return PTR_ERR(priv->ref_clk_m); + } + + priv->ref_clk_n = devm_clk_get(dev, "refclk_n"); + if (IS_ERR(priv->ref_clk_n)) { + dev_err(dev, "failed to find ref clock N\n"); + return PTR_ERR(priv->ref_clk_n); + } + + priv->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(priv->pclk)) { + dev_err(dev, "failed to find pclk\n"); + return PTR_ERR(priv->pclk); + } + + dev_set_drvdata(dev, priv); + phy_set_drvdata(priv->phy, priv); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id rockchip_p3phy_of_match[] = { + { .compatible = "rockchip,rk3568-pcie3-phy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, rockchip_p3phy_of_match); + +static struct platform_driver rockchip_p3phy_driver = { + .probe = rockchip_p3phy_probe, + .driver = { + .name = "rockchip-snps-pcie3-phy", + .of_match_table = rockchip_p3phy_of_match, + }, +}; +module_platform_driver(rockchip_p3phy_driver); +MODULE_DESCRIPTION("Rockchip Synopsys PCIe 3.0 PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index f3286f4cd306..cd0110f4b016 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -41,6 +41,9 @@ enum phy_mode { PHY_MODE_MIPI_DPHY, PHY_MODE_SATA, PHY_MODE_LVDS, + PHY_MODE_PCIE_RC, + PHY_MODE_PCIE_EP, + PHY_MODE_PCIE_BIFURCATION, PHY_MODE_DP };