From e3e12f52b214121840b44c91de5b3e5af5d3eb84 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 06 Nov 2023 03:04:41 +0000 Subject: [PATCH] rk3568 rt init --- kernel/drivers/pci/controller/dwc/pcie-dw-rockchip.c | 162 ++++++++++++++++++++++++++++++++++++++++++----------- 1 files changed, 128 insertions(+), 34 deletions(-) diff --git a/kernel/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/kernel/drivers/pci/controller/dwc/pcie-dw-rockchip.c index 0dce4b6..33b6dc3 100644 --- a/kernel/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/kernel/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -34,6 +34,7 @@ #include <linux/regmap.h> #include <linux/reset.h> #include <linux/resource.h> +#include <linux/rfkill-wlan.h> #include <linux/signal.h> #include <linux/types.h> #include <linux/uaccess.h> @@ -53,6 +54,8 @@ const char *id; struct reset_control *rst; }; + +#define RK_PCIE_DBG 0 #define PCIE_DMA_OFFSET 0x380000 @@ -120,7 +123,6 @@ #define PCIE_CLIENT_DBG_FIFO_STATUS 0x350 #define PCIE_CLIENT_DBG_TRANSITION_DATA 0xffff0000 #define PCIE_CLIENT_DBF_EN 0xffff0003 -#define RK_PCIE_DBG 0 #define PCIE_PHY_LINKUP BIT(0) #define PCIE_DATA_LINKUP BIT(1) @@ -147,6 +149,7 @@ unsigned int clk_cnt; struct reset_bulk_data *rsts; struct gpio_desc *rst_gpio; + struct gpio_desc *prsnt_gpio; phys_addr_t mem_start; size_t mem_size; struct pcie_port pp; @@ -154,6 +157,7 @@ struct regmap *pmu_grf; struct dma_trx_obj *dma_obj; bool in_suspend; + bool skip_scan_in_resume; bool is_rk1808; bool is_signal_test; bool bifurcation; @@ -161,10 +165,12 @@ struct irq_domain *irq_domain; raw_spinlock_t intx_lock; struct dentry *debugfs; + u32 msi_vector_num; }; struct rk_pcie_of_data { enum rk_pcie_device_mode mode; + u32 msi_vector_num; }; #define to_rk_pcie(x) dev_get_drvdata((x)->dev) @@ -410,8 +416,7 @@ return 1; } else { val = rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_LTSSM_STATUS); - if ((val & (RDLH_LINKUP | SMLH_LINKUP)) == 0x30000 && - (val & GENMASK(5, 0)) == 0x11) + if ((val & (RDLH_LINKUP | SMLH_LINKUP)) == 0x30000) return 1; } @@ -453,10 +458,16 @@ static int rk_pcie_establish_link(struct dw_pcie *pci) { - int retries; + int retries, power; struct rk_pcie *rk_pcie = to_rk_pcie(pci); + bool std_rc = rk_pcie->mode == RK_PCIE_RC_TYPE && !rk_pcie->dma_obj; - if (dw_pcie_link_up(pci)) { + /* + * For standard RC, even if the link has been setup by firmware, + * we still need to reset link as we need to remove all resource info + * from devices, for instance BAR, as it wasn't assigned by kernel. + */ + if (dw_pcie_link_up(pci) && !std_rc) { dev_err(pci->dev, "link is already up\n"); return 0; } @@ -472,6 +483,21 @@ rk_pcie_enable_ltssm(rk_pcie); /* + * In resume routine, function devices' resume function must be late after + * controllers'. Some devices, such as Wi-Fi, need special IO setting before + * finishing training. So there must be timeout here. These kinds of devices + * need rescan devices by its driver when used. So no need to waste time waiting + * for training pass. + */ + if (rk_pcie->in_suspend && rk_pcie->skip_scan_in_resume) { + rfkill_get_wifi_power_state(&power); + if (!power) { + gpiod_set_value_cansleep(rk_pcie->rst_gpio, 1); + return 0; + } + } + + /* * PCIe requires the refclk to be stable for 100µs prior to releasing * PERST and T_PVPERL (Power stable to PERST# inactive) should be a * minimum of 100ms. See table 2-4 in section 2.6.2 AC, the PCI Express @@ -480,6 +506,12 @@ */ msleep(1000); gpiod_set_value_cansleep(rk_pcie->rst_gpio, 1); + + /* + * Add this 1ms delay because we observe link is always up stably after it and + * could help us save 20ms for scanning devices. + */ + usleep_range(1000, 1100); for (retries = 0; retries < 10; retries++) { if (dw_pcie_link_up(pci)) { @@ -516,7 +548,7 @@ return -EINVAL; } - rk_pcie->dma_obj = pcie_dw_dmatest_register(rk_pcie->pci, true); + rk_pcie->dma_obj = pcie_dw_dmatest_register(rk_pcie->pci->dev, true); if (IS_ERR(rk_pcie->dma_obj)) { dev_err(rk_pcie->pci->dev, "failed to prepare dmatest\n"); return -EINVAL; @@ -675,6 +707,14 @@ return 0; } +static void rk_pcie_msi_set_num_vectors(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct rk_pcie *rk_pcie = to_rk_pcie(pci); + + pp->num_vectors = rk_pcie->msi_vector_num; +} + static int rk_pcie_host_init(struct pcie_port *pp) { int ret; @@ -707,6 +747,9 @@ if (pp->msi_irq < 0) { dev_info(dev, "use outband MSI support"); rk_pcie_host_ops.msi_host_init = rk_pcie_msi_host_init; + } else { + dev_info(dev, "max MSI vector is %d\n", rk_pcie->msi_vector_num); + rk_pcie_host_ops.set_num_vectors = rk_pcie_msi_set_num_vectors; } } @@ -864,6 +907,10 @@ dev_err(&pdev->dev, "invalid reset-gpios property in node\n"); return PTR_ERR(rk_pcie->rst_gpio); } + + rk_pcie->prsnt_gpio = devm_gpiod_get_optional(&pdev->dev, "prsnt", GPIOD_IN); + if (IS_ERR_OR_NULL(rk_pcie->prsnt_gpio)) + dev_info(&pdev->dev, "invalid prsnt-gpios property in node\n"); return 0; } @@ -1149,6 +1196,11 @@ .mode = RK_PCIE_EP_TYPE, }; +static const struct rk_pcie_of_data rk3528_pcie_rc_of_data = { + .mode = RK_PCIE_RC_TYPE, + .msi_vector_num = 8, +}; + static const struct of_device_id rk_pcie_of_match[] = { { .compatible = "rockchip,rk1808-pcie", @@ -1157,6 +1209,10 @@ { .compatible = "rockchip,rk1808-pcie-ep", .data = &rk_pcie_ep_of_data, + }, + { + .compatible = "rockchip,rk3528-pcie", + .data = &rk3528_pcie_rc_of_data, }, { .compatible = "rockchip,rk3568-pcie", @@ -1529,28 +1585,35 @@ enum rk_pcie_device_mode mode; struct device_node *np = pdev->dev.of_node; struct platform_driver *drv = to_platform_driver(dev->driver); - u32 val; + u32 val = 0; int irq; match = of_match_device(rk_pcie_of_match, dev); - if (!match) - return -EINVAL; + if (!match) { + ret = -EINVAL; + goto release_driver; + } data = (struct rk_pcie_of_data *)match->data; mode = (enum rk_pcie_device_mode)data->mode; rk_pcie = devm_kzalloc(dev, sizeof(*rk_pcie), GFP_KERNEL); - if (!rk_pcie) - return -ENOMEM; + if (!rk_pcie) { + ret = -ENOMEM; + goto release_driver; + } pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); - if (!pci) - return -ENOMEM; + if (!pci) { + ret = -ENOMEM; + goto release_driver; + } pci->dev = dev; pci->ops = &dw_pcie_ops; rk_pcie->mode = mode; + rk_pcie->msi_vector_num = data->msi_vector_num; rk_pcie->pci = pci; if (of_device_is_compatible(np, "rockchip,rk1808-pcie") || @@ -1565,20 +1628,38 @@ ret = rk_pcie_resource_get(pdev, rk_pcie); if (ret) { dev_err(dev, "resource init failed\n"); - return ret; + goto release_driver; } + if (!IS_ERR_OR_NULL(rk_pcie->prsnt_gpio)) { + if (!gpiod_get_value(rk_pcie->prsnt_gpio)) { + ret = -ENODEV; + goto release_driver; + } + } + +retry_regulator: /* DON'T MOVE ME: must be enable before phy init */ rk_pcie->vpcie3v3 = devm_regulator_get_optional(dev, "vpcie3v3"); if (IS_ERR(rk_pcie->vpcie3v3)) { - if (PTR_ERR(rk_pcie->vpcie3v3) != -ENODEV) - return PTR_ERR(rk_pcie->vpcie3v3); + if (PTR_ERR(rk_pcie->vpcie3v3) != -ENODEV) { + if (IS_ENABLED(CONFIG_PCIE_RK_THREADED_INIT)) { + /* Deferred but in threaded context for most 10s */ + msleep(20); + if (++val < 500) + goto retry_regulator; + } + + ret = PTR_ERR(rk_pcie->vpcie3v3); + goto release_driver; + } + dev_info(dev, "no vpcie3v3 regulator found\n"); } ret = rk_pcie_enable_power(rk_pcie); if (ret) - return ret; + goto release_driver; ret = rk_pcie_phy_init(rk_pcie); if (ret) { @@ -1589,13 +1670,13 @@ ret = rk_pcie_reset_control_release(rk_pcie); if (ret) { dev_err(dev, "reset control init failed\n"); - goto disable_vpcie3v3; + goto disable_phy; } ret = rk_pcie_request_sys_irq(rk_pcie, pdev); if (ret) { dev_err(dev, "pcie irq init failed\n"); - goto disable_vpcie3v3; + goto disable_phy; } platform_set_drvdata(pdev, rk_pcie); @@ -1603,7 +1684,7 @@ ret = rk_pcie_clk_init(rk_pcie); if (ret) { dev_err(dev, "clock init failed\n"); - goto disable_vpcie3v3; + goto disable_phy; } dw_pcie_dbi_ro_wr_en(pci); @@ -1626,9 +1707,9 @@ /* Unmask all legacy interrupt from INTA~INTD */ rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_INTR_MASK_LEGACY, UNMASK_ALL_LEGACY_INT); + } else { + dev_info(dev, "missing legacy IRQ resource\n"); } - - dev_info(dev, "missing legacy IRQ resource\n"); } /* Set PCIe mode */ @@ -1649,6 +1730,10 @@ dw_pcie_writel_dbi(pci, PCIE_CAP_LINK_CONTROL2_LINK_STATUS, val); rk_pcie->is_signal_test = true; } + + /* Skip waiting for training to pass in system PM routine */ + if (device_property_read_bool(dev, "rockchip,skip-scan-in-resume")) + rk_pcie->skip_scan_in_resume = true; switch (rk_pcie->mode) { case RK_PCIE_RC_TYPE: @@ -1709,24 +1794,36 @@ remove_irq_domain: if (rk_pcie->irq_domain) irq_domain_remove(rk_pcie->irq_domain); +disable_phy: + phy_power_off(rk_pcie->phy); + phy_exit(rk_pcie->phy); deinit_clk: rk_pcie_clk_deinit(rk_pcie); disable_vpcie3v3: rk_pcie_disable_power(rk_pcie); + +release_driver: + if (IS_ENABLED(CONFIG_PCIE_RK_THREADED_INIT)) + device_release_driver(dev); return ret; } static int rk_pcie_probe(struct platform_device *pdev) { - struct task_struct *tsk; + if (IS_ENABLED(CONFIG_PCIE_RK_THREADED_INIT)) { + struct task_struct *tsk; - tsk = kthread_run(rk_pcie_really_probe, pdev, "rk-pcie"); - if (IS_ERR(tsk)) { - dev_err(&pdev->dev, "start rk-pcie thread failed\n"); - return PTR_ERR(tsk); + tsk = kthread_run(rk_pcie_really_probe, pdev, "rk-pcie"); + if (IS_ERR(tsk)) { + dev_err(&pdev->dev, "start rk-pcie thread failed\n"); + return PTR_ERR(tsk); + } + + return 0; } - return 0; + + return rk_pcie_really_probe(pdev); } static int __maybe_unused rockchip_dw_pcie_suspend(struct device *dev) @@ -1749,9 +1846,6 @@ gpiod_set_value_cansleep(rk_pcie->rst_gpio, 0); ret = rk_pcie_disable_power(rk_pcie); - - if (rk_pcie->pci->pp.msi_irq > 0) - dw_pcie_free_msi(&rk_pcie->pci->pp); return ret; } @@ -1811,9 +1905,6 @@ goto err; } - if (rk_pcie->pci->pp.msi_irq > 0) - dw_pcie_msi_init(&rk_pcie->pci->pp); - if (std_rc) goto std_rc_done; @@ -1836,6 +1927,9 @@ goto err; } + if (rk_pcie->pci->pp.msi_irq > 0) + dw_pcie_msi_init(&rk_pcie->pci->pp); + return 0; err: rk_pcie_disable_power(rk_pcie); -- Gitblit v1.6.2