13d0407baSopenharmony_ci// SPDX-License-Identifier: GPL-2.0 23d0407baSopenharmony_ci/* 33d0407baSopenharmony_ci * Rockchip PCIE3.0 phy driver 43d0407baSopenharmony_ci * 53d0407baSopenharmony_ci * Copyright (C) 2020 Rockchip Electronics Co., Ltd. 63d0407baSopenharmony_ci */ 73d0407baSopenharmony_ci 83d0407baSopenharmony_ci#include <linux/clk.h> 93d0407baSopenharmony_ci#include <linux/delay.h> 103d0407baSopenharmony_ci#include <linux/io.h> 113d0407baSopenharmony_ci#include <linux/iopoll.h> 123d0407baSopenharmony_ci#include <linux/kernel.h> 133d0407baSopenharmony_ci#include <linux/mfd/syscon.h> 143d0407baSopenharmony_ci#include <linux/module.h> 153d0407baSopenharmony_ci#include <linux/of_device.h> 163d0407baSopenharmony_ci#include <linux/phy/pcie.h> 173d0407baSopenharmony_ci#include <linux/phy/phy.h> 183d0407baSopenharmony_ci#include <linux/regmap.h> 193d0407baSopenharmony_ci#include <linux/reset.h> 203d0407baSopenharmony_ci#include <dt-bindings/phy/phy-snps-pcie3.h> 213d0407baSopenharmony_ci 223d0407baSopenharmony_ci/* Register for RK3568 */ 233d0407baSopenharmony_ci#define GRF_PCIE30PHY_CON1 0x4 243d0407baSopenharmony_ci#define GRF_PCIE30PHY_CON6 0x18 253d0407baSopenharmony_ci#define GRF_PCIE30PHY_CON9 0x24 263d0407baSopenharmony_ci#define GRF_PCIE30PHY_STATUS0 0x80 273d0407baSopenharmony_ci#define SRAM_INIT_DONE(reg) ((reg)&BIT(14)) 283d0407baSopenharmony_ci 293d0407baSopenharmony_ci/* Register for RK3588 */ 303d0407baSopenharmony_ci#define PHP_GRF_PCIESEL_CON 0x100 313d0407baSopenharmony_ci#define RK3588_PCIE3PHY_GRF_CMN_CON0 0x0 323d0407baSopenharmony_ci#define RK3588_PCIE3PHY_GRF_PHY0_STATUS1 0x904 333d0407baSopenharmony_ci#define RK3588_PCIE3PHY_GRF_PHY1_STATUS1 0xa04 343d0407baSopenharmony_ci#define RK3588_SRAM_INIT_DONE(reg) ((reg)&BIT(0)) 353d0407baSopenharmony_ci 363d0407baSopenharmony_ci#define DEBUGER_INDEX_TH 3 373d0407baSopenharmony_ci#define DEBUGER_INDEX_FO 4 383d0407baSopenharmony_ci#define DEBUGER_INDEX_EI 8 393d0407baSopenharmony_ci#define DEBUGER_INDEX_FIF 15 403d0407baSopenharmony_ci#define DEBUGER_INDEX_SIX 16 413d0407baSopenharmony_ci#define DEBUGER_INDEX_TWEF 24 423d0407baSopenharmony_ci#define DEBUGER_INDEX_THIO 31 433d0407baSopenharmony_ci#define SRAM_NODE_VALUE 500 443d0407baSopenharmony_ci 453d0407baSopenharmony_cistruct rockchip_p3phy_ops; 463d0407baSopenharmony_ci 473d0407baSopenharmony_cistruct rockchip_p3phy_priv { 483d0407baSopenharmony_ci const struct rockchip_p3phy_ops *ops; 493d0407baSopenharmony_ci void __iomem *mmio; 503d0407baSopenharmony_ci /* mode: RC, EP */ 513d0407baSopenharmony_ci int mode; 523d0407baSopenharmony_ci /* pcie30_phymode: Aggregation, Bifurcation */ 533d0407baSopenharmony_ci int pcie30_phymode; 543d0407baSopenharmony_ci struct regmap *phy_grf; 553d0407baSopenharmony_ci struct regmap *pipe_grf; 563d0407baSopenharmony_ci struct reset_control *p30phy; 573d0407baSopenharmony_ci struct phy *phy; 583d0407baSopenharmony_ci struct clk_bulk_data *clks; 593d0407baSopenharmony_ci int num_clks; 603d0407baSopenharmony_ci bool is_bifurcation; 613d0407baSopenharmony_ci}; 623d0407baSopenharmony_ci 633d0407baSopenharmony_cistruct rockchip_p3phy_ops { 643d0407baSopenharmony_ci int (*phy_init)(struct rockchip_p3phy_priv *priv); 653d0407baSopenharmony_ci}; 663d0407baSopenharmony_ci 673d0407baSopenharmony_cistatic int rockchip_p3phy_set_mode(struct phy *phy, enum phy_mode mode, int submode) 683d0407baSopenharmony_ci{ 693d0407baSopenharmony_ci struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy); 703d0407baSopenharmony_ci 713d0407baSopenharmony_ci /* Acutally We don't care EP/RC mode, but just record it */ 723d0407baSopenharmony_ci switch (submode) { 733d0407baSopenharmony_ci case PHY_MODE_PCIE_RC: 743d0407baSopenharmony_ci priv->mode = PHY_MODE_PCIE_RC; 753d0407baSopenharmony_ci break; 763d0407baSopenharmony_ci case PHY_MODE_PCIE_EP: 773d0407baSopenharmony_ci priv->mode = PHY_MODE_PCIE_EP; 783d0407baSopenharmony_ci break; 793d0407baSopenharmony_ci case PHY_MODE_PCIE_BIFURCATION: 803d0407baSopenharmony_ci priv->is_bifurcation = true; 813d0407baSopenharmony_ci break; 823d0407baSopenharmony_ci default: 833d0407baSopenharmony_ci pr_info("%s, invalid mode\n", __func__); 843d0407baSopenharmony_ci return -EINVAL; 853d0407baSopenharmony_ci } 863d0407baSopenharmony_ci 873d0407baSopenharmony_ci return 0; 883d0407baSopenharmony_ci} 893d0407baSopenharmony_ci 903d0407baSopenharmony_cistatic int rockchip_p3phy_rk3568_init(struct rockchip_p3phy_priv *priv) 913d0407baSopenharmony_ci{ 923d0407baSopenharmony_ci int ret = 0; 933d0407baSopenharmony_ci u32 reg; 943d0407baSopenharmony_ci 953d0407baSopenharmony_ci /* Deassert PCIe PMA output clamp mode */ 963d0407baSopenharmony_ci regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON9, (0x1 << DEBUGER_INDEX_FIF) | (0x1 << DEBUGER_INDEX_THIO)); 973d0407baSopenharmony_ci /* Set bifurcation if needed, and it doesn't care RC/EP */ 983d0407baSopenharmony_ci if (priv->is_bifurcation) { 993d0407baSopenharmony_ci regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON6, 0x1 | (0xf << DEBUGER_INDEX_SIX)); 1003d0407baSopenharmony_ci regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON1, (0x1 << DEBUGER_INDEX_FIF) | (0x1 << DEBUGER_INDEX_THIO)); 1013d0407baSopenharmony_ci } 1023d0407baSopenharmony_ci 1033d0407baSopenharmony_ci reset_control_deassert(priv->p30phy); 1043d0407baSopenharmony_ci 1053d0407baSopenharmony_ci ret = regmap_read_poll_timeout(priv->phy_grf, GRF_PCIE30PHY_STATUS0, reg, SRAM_INIT_DONE(reg), 0, SRAM_NODE_VALUE); 1063d0407baSopenharmony_ci if (ret) { 1073d0407baSopenharmony_ci pr_err("%s: lock failed 0x%x, check input refclk and power supply\n", __func__, reg); 1083d0407baSopenharmony_ci } 1093d0407baSopenharmony_ci return ret; 1103d0407baSopenharmony_ci} 1113d0407baSopenharmony_ci 1123d0407baSopenharmony_cistatic const struct rockchip_p3phy_ops rk3568_ops = { 1133d0407baSopenharmony_ci .phy_init = rockchip_p3phy_rk3568_init, 1143d0407baSopenharmony_ci}; 1153d0407baSopenharmony_ci 1163d0407baSopenharmony_cistatic int rockchip_p3phy_rk3588_init(struct rockchip_p3phy_priv *priv) 1173d0407baSopenharmony_ci{ 1183d0407baSopenharmony_ci int ret = 0; 1193d0407baSopenharmony_ci u32 reg; 1203d0407baSopenharmony_ci 1213d0407baSopenharmony_ci /* Deassert PCIe PMA output clamp mode */ 1223d0407baSopenharmony_ci regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_CMN_CON0, (0x1 << DEBUGER_INDEX_EI) | (0x1 << DEBUGER_INDEX_TWEF)); 1233d0407baSopenharmony_ci 1243d0407baSopenharmony_ci reset_control_deassert(priv->p30phy); 1253d0407baSopenharmony_ci 1263d0407baSopenharmony_ci ret = regmap_read_poll_timeout(priv->phy_grf, RK3588_PCIE3PHY_GRF_PHY0_STATUS1, reg, RK3588_SRAM_INIT_DONE(reg), 0, 1273d0407baSopenharmony_ci SRAM_NODE_VALUE); 1283d0407baSopenharmony_ci ret |= regmap_read_poll_timeout(priv->phy_grf, RK3588_PCIE3PHY_GRF_PHY1_STATUS1, reg, RK3588_SRAM_INIT_DONE(reg), 0, 1293d0407baSopenharmony_ci SRAM_NODE_VALUE); 1303d0407baSopenharmony_ci if (ret) { 1313d0407baSopenharmony_ci pr_err("%s: lock failed 0x%x, check input refclk and power supply\n", __func__, reg); 1323d0407baSopenharmony_ci } 1333d0407baSopenharmony_ci return ret; 1343d0407baSopenharmony_ci} 1353d0407baSopenharmony_ci 1363d0407baSopenharmony_cistatic const struct rockchip_p3phy_ops rk3588_ops = { 1373d0407baSopenharmony_ci .phy_init = rockchip_p3phy_rk3588_init, 1383d0407baSopenharmony_ci}; 1393d0407baSopenharmony_ci 1403d0407baSopenharmony_cistatic int rochchip_p3phy_init(struct phy *phy) 1413d0407baSopenharmony_ci{ 1423d0407baSopenharmony_ci struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy); 1433d0407baSopenharmony_ci int ret; 1443d0407baSopenharmony_ci 1453d0407baSopenharmony_ci ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks); 1463d0407baSopenharmony_ci if (ret) { 1473d0407baSopenharmony_ci pr_err("failed to enable PCIe bulk clks %d\n", ret); 1483d0407baSopenharmony_ci return ret; 1493d0407baSopenharmony_ci } 1503d0407baSopenharmony_ci 1513d0407baSopenharmony_ci reset_control_assert(priv->p30phy); 1523d0407baSopenharmony_ci udelay(1); 1533d0407baSopenharmony_ci 1543d0407baSopenharmony_ci if (priv->ops->phy_init) { 1553d0407baSopenharmony_ci ret = priv->ops->phy_init(priv); 1563d0407baSopenharmony_ci if (ret) { 1573d0407baSopenharmony_ci clk_bulk_disable_unprepare(priv->num_clks, priv->clks); 1583d0407baSopenharmony_ci } 1593d0407baSopenharmony_ci }; 1603d0407baSopenharmony_ci 1613d0407baSopenharmony_ci return ret; 1623d0407baSopenharmony_ci} 1633d0407baSopenharmony_ci 1643d0407baSopenharmony_cistatic int rochchip_p3phy_exit(struct phy *phy) 1653d0407baSopenharmony_ci{ 1663d0407baSopenharmony_ci struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy); 1673d0407baSopenharmony_ci clk_bulk_disable_unprepare(priv->num_clks, priv->clks); 1683d0407baSopenharmony_ci reset_control_assert(priv->p30phy); 1693d0407baSopenharmony_ci return 0; 1703d0407baSopenharmony_ci} 1713d0407baSopenharmony_ci 1723d0407baSopenharmony_cistatic const struct phy_ops rochchip_p3phy_ops = { 1733d0407baSopenharmony_ci .init = rochchip_p3phy_init, 1743d0407baSopenharmony_ci .exit = rochchip_p3phy_exit, 1753d0407baSopenharmony_ci .set_mode = rockchip_p3phy_set_mode, 1763d0407baSopenharmony_ci .owner = THIS_MODULE, 1773d0407baSopenharmony_ci}; 1783d0407baSopenharmony_ci 1793d0407baSopenharmony_cistatic int rockchip_p3phy_probe(struct platform_device *pdev) 1803d0407baSopenharmony_ci{ 1813d0407baSopenharmony_ci struct phy_provider *phy_provider; 1823d0407baSopenharmony_ci struct device *dev = &pdev->dev; 1833d0407baSopenharmony_ci struct rockchip_p3phy_priv *priv; 1843d0407baSopenharmony_ci struct device_node *np = dev->of_node; 1853d0407baSopenharmony_ci struct resource *res; 1863d0407baSopenharmony_ci int ret; 1873d0407baSopenharmony_ci u32 val, reg; 1883d0407baSopenharmony_ci 1893d0407baSopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 1903d0407baSopenharmony_ci if (!priv) { 1913d0407baSopenharmony_ci return -ENOMEM; 1923d0407baSopenharmony_ci } 1933d0407baSopenharmony_ci 1943d0407baSopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1953d0407baSopenharmony_ci priv->mmio = devm_ioremap_resource(dev, res); 1963d0407baSopenharmony_ci if (IS_ERR(priv->mmio)) { 1973d0407baSopenharmony_ci ret = PTR_ERR(priv->mmio); 1983d0407baSopenharmony_ci return ret; 1993d0407baSopenharmony_ci } 2003d0407baSopenharmony_ci 2013d0407baSopenharmony_ci priv->ops = of_device_get_match_data(&pdev->dev); 2023d0407baSopenharmony_ci if (!priv->ops) { 2033d0407baSopenharmony_ci dev_err(&pdev->dev, "no of match data provided\n"); 2043d0407baSopenharmony_ci return -EINVAL; 2053d0407baSopenharmony_ci } 2063d0407baSopenharmony_ci 2073d0407baSopenharmony_ci priv->phy_grf = syscon_regmap_lookup_by_phandle(np, "rockchip,phy-grf"); 2083d0407baSopenharmony_ci if (IS_ERR(priv->phy_grf)) { 2093d0407baSopenharmony_ci dev_err(dev, "failed to find rockchip,phy_grf regmap\n"); 2103d0407baSopenharmony_ci return PTR_ERR(priv->phy_grf); 2113d0407baSopenharmony_ci } 2123d0407baSopenharmony_ci 2133d0407baSopenharmony_ci priv->pipe_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,pipe-grf"); 2143d0407baSopenharmony_ci if (IS_ERR(priv->pipe_grf)) { 2153d0407baSopenharmony_ci dev_info(dev, "failed to find rockchip,pipe_grf regmap\n"); 2163d0407baSopenharmony_ci } 2173d0407baSopenharmony_ci 2183d0407baSopenharmony_ci ret = device_property_read_u32(dev, "rockchip,pcie30-phymode", &val); 2193d0407baSopenharmony_ci if (!ret) { 2203d0407baSopenharmony_ci priv->pcie30_phymode = val; 2213d0407baSopenharmony_ci } else { 2223d0407baSopenharmony_ci priv->pcie30_phymode = PHY_MODE_PCIE_AGGREGATION; 2233d0407baSopenharmony_ci } 2243d0407baSopenharmony_ci 2253d0407baSopenharmony_ci /* Select correct pcie30_phymode */ 2263d0407baSopenharmony_ci if (priv->pcie30_phymode > DEBUGER_INDEX_FO) { 2273d0407baSopenharmony_ci priv->pcie30_phymode = PHY_MODE_PCIE_AGGREGATION; 2283d0407baSopenharmony_ci } 2293d0407baSopenharmony_ci 2303d0407baSopenharmony_ci regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_CMN_CON0, (0x7 << DEBUGER_INDEX_SIX) | priv->pcie30_phymode); 2313d0407baSopenharmony_ci 2323d0407baSopenharmony_ci /* Set pcie1ln_sel in PHP_GRF_PCIESEL_CON */ 2333d0407baSopenharmony_ci if (!IS_ERR(priv->pipe_grf)) { 2343d0407baSopenharmony_ci reg = priv->pcie30_phymode & DEBUGER_INDEX_TH; 2353d0407baSopenharmony_ci if (reg) { 2363d0407baSopenharmony_ci regmap_write(priv->pipe_grf, PHP_GRF_PCIESEL_CON, (reg << DEBUGER_INDEX_SIX) | reg); 2373d0407baSopenharmony_ci } 2383d0407baSopenharmony_ci }; 2393d0407baSopenharmony_ci 2403d0407baSopenharmony_ci priv->phy = devm_phy_create(dev, NULL, &rochchip_p3phy_ops); 2413d0407baSopenharmony_ci if (IS_ERR(priv->phy)) { 2423d0407baSopenharmony_ci dev_err(dev, "failed to create combphy\n"); 2433d0407baSopenharmony_ci return PTR_ERR(priv->phy); 2443d0407baSopenharmony_ci } 2453d0407baSopenharmony_ci 2463d0407baSopenharmony_ci priv->p30phy = devm_reset_control_get(dev, "phy"); 2473d0407baSopenharmony_ci if (IS_ERR(priv->p30phy)) { 2483d0407baSopenharmony_ci dev_warn(dev, "no phy reset control specified\n"); 2493d0407baSopenharmony_ci priv->p30phy = NULL; 2503d0407baSopenharmony_ci } 2513d0407baSopenharmony_ci 2523d0407baSopenharmony_ci priv->num_clks = devm_clk_bulk_get_all(dev, &priv->clks); 2533d0407baSopenharmony_ci if (priv->num_clks < 1) { 2543d0407baSopenharmony_ci return -ENODEV; 2553d0407baSopenharmony_ci } 2563d0407baSopenharmony_ci 2573d0407baSopenharmony_ci dev_set_drvdata(dev, priv); 2583d0407baSopenharmony_ci phy_set_drvdata(priv->phy, priv); 2593d0407baSopenharmony_ci phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 2603d0407baSopenharmony_ci return PTR_ERR_OR_ZERO(phy_provider); 2613d0407baSopenharmony_ci} 2623d0407baSopenharmony_ci 2633d0407baSopenharmony_cistatic const struct of_device_id rockchip_p3phy_of_match[] = { 2643d0407baSopenharmony_ci {.compatible = "rockchip,rk3568-pcie3-phy", .data = &rk3568_ops}, 2653d0407baSopenharmony_ci {.compatible = "rockchip,rk3588-pcie3-phy", .data = &rk3588_ops}, 2663d0407baSopenharmony_ci {}, 2673d0407baSopenharmony_ci}; 2683d0407baSopenharmony_ciMODULE_DEVICE_TABLE(of, rockchip_p3phy_of_match); 2693d0407baSopenharmony_ci 2703d0407baSopenharmony_cistatic struct platform_driver rockchip_p3phy_driver = { 2713d0407baSopenharmony_ci .probe = rockchip_p3phy_probe, 2723d0407baSopenharmony_ci .driver = 2733d0407baSopenharmony_ci { 2743d0407baSopenharmony_ci .name = "rockchip-snps-pcie3-phy", 2753d0407baSopenharmony_ci .of_match_table = rockchip_p3phy_of_match, 2763d0407baSopenharmony_ci }, 2773d0407baSopenharmony_ci}; 2783d0407baSopenharmony_cimodule_platform_driver(rockchip_p3phy_driver); 2793d0407baSopenharmony_ciMODULE_DESCRIPTION("Rockchip Synopsys PCIe 3.0 PHY driver"); 2803d0407baSopenharmony_ciMODULE_LICENSE("GPL v2"); 281