From f45e756958099c35d6afb746df1d40a1c6302cfc Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Tue, 07 Nov 2023 06:20:23 +0000
Subject: [PATCH] enable wifi gpio
---
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