From 85abcd6ea60f14b662103fda6fda07bdd060f351 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 21 Oct 2020 15:06:50 +0800 Subject: [PATCH] phy: phy-rockchip-snps-pcie3: Initial support This patch adds initial support for snps pcie 3.0 phy. Change-Id: I23d0750a60ffde30f434e1c676916d4bc4772400 Signed-off-by: Shawn Lin --- drivers/phy/rockchip/Kconfig | 9 + drivers/phy/rockchip/Makefile | 1 + .../phy/rockchip/phy-rockchip-snps-pcie3.c | 213 ++++++++++++++++++ 3 files changed, 223 insertions(+) create mode 100644 drivers/phy/rockchip/phy-rockchip-snps-pcie3.c diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig index 58e244059741..b300775ffcf7 100644 --- a/drivers/phy/rockchip/Kconfig +++ b/drivers/phy/rockchip/Kconfig @@ -100,6 +100,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 442ad43919ad..9482447d0581 100644 --- a/drivers/phy/rockchip/Makefile +++ b/drivers/phy/rockchip/Makefile @@ -11,5 +11,6 @@ obj-$(CONFIG_PHY_ROCKCHIP_INNO_VIDEO_PHY) += phy-rockchip-inno-video-phy.o obj-$(CONFIG_PHY_ROCKCHIP_MIPI_RX) += phy-rockchip-mipi-rx.o obj-$(CONFIG_PHY_ROCKCHIP_NANENG_USB2) += phy-rockchip-naneng-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..15a0fe1860cc --- /dev/null +++ b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c @@ -0,0 +1,213 @@ +// 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 + +struct rockchip_p3phy_priv { + void __iomem *mmio; + int mode; + struct regmap *phy_grf; + struct reset_control *p30phy; + struct reset_control *p30x1; + struct reset_control *p30x2; + 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) +{ + 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; + + 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); + + /* 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); + udelay(1); + reset_control_assert(priv->p30x1); + reset_control_assert(priv->p30x2); + udelay(1); + reset_control_deassert(priv->p30x1); + reset_control_deassert(priv->p30x2); + + 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->p30x1); + reset_control_assert(priv->p30x2); + 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->p30x1 = devm_reset_control_get(dev, "p30x1"); + if (IS_ERR(priv->p30x1)) { + dev_warn(dev, "no p30x1 reset control specified\n"); + priv->p30x1 = NULL; + } + + priv->p30x2 = devm_reset_control_get(dev, "p30x2"); + if (IS_ERR(priv->p30x2)) { + dev_warn(dev, "no p30x2 reset control specified\n"); + priv->p30x2 = 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");