From ee930fffee469d076998274a2ca55e13dc1efb67 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Fri, 10 May 2024 08:50:54 +0000
Subject: [PATCH] enable tun/tap/iptables

---
 kernel/drivers/pci/controller/dwc/pcie-dw-rockchip.c |  272 ++++++++++++++++++++++++++++++++++--------------------
 1 files changed, 171 insertions(+), 101 deletions(-)

diff --git a/kernel/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/kernel/drivers/pci/controller/dwc/pcie-dw-rockchip.c
index 9b89741..e22295e 100644
--- a/kernel/drivers/pci/controller/dwc/pcie-dw-rockchip.c
+++ b/kernel/drivers/pci/controller/dwc/pcie-dw-rockchip.c
@@ -8,6 +8,7 @@
  * Author: Simon Xue <xxm@rock-chips.com>
  */
 
+#include <dt-bindings/phy/phy.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/fs.h>
@@ -115,6 +116,10 @@
 #define PME_TURN_OFF			(BIT(4) | BIT(20))
 #define PCIE_CLIENT_GENERAL_DEBUG	0x104
 #define PCIE_CLIENT_HOT_RESET_CTRL	0x180
+#define PCIE_LTSSM_APP_DLY1_EN		BIT(0)
+#define PCIE_LTSSM_APP_DLY2_EN		BIT(1)
+#define PCIE_LTSSM_APP_DLY1_DONE	BIT(2)
+#define PCIE_LTSSM_APP_DLY2_DONE	BIT(3)
 #define PCIE_LTSSM_ENABLE_ENHANCE	BIT(4)
 #define PCIE_CLIENT_LTSSM_STATUS	0x300
 #define SMLH_LINKUP			BIT(16)
@@ -137,6 +142,8 @@
 
 #define PCIE_PL_ORDER_RULE_CTRL_OFF	0x8B4
 #define RK_PCIE_L2_TMOUT_US		5000
+#define RK_PCIE_HOTRESET_TMOUT_US	10000
+#define RK_PCIE_ENUM_HW_RETRYIES	2
 
 enum rk_pcie_ltssm_code {
 	S_L0 = 0x11,
@@ -185,6 +192,10 @@
 	u32				l1ss_ctl1;
 	struct dentry			*debugfs;
 	u32				msi_vector_num;
+	struct workqueue_struct		*hot_rst_wq;
+	struct work_struct		hot_rst_work;
+	u32				comp_prst[2];
+	u32				intx;
 };
 
 struct rk_pcie_of_data {
@@ -193,6 +204,8 @@
 };
 
 #define to_rk_pcie(x)	dev_get_drvdata((x)->dev)
+static int rk_pcie_disable_power(struct rk_pcie *rk_pcie);
+static int rk_pcie_enable_power(struct rk_pcie *rk_pcie);
 
 static int rk_pcie_read(void __iomem *addr, int size, u32 *val)
 {
@@ -693,24 +706,6 @@
 	rk_pcie_writel_apb(rk_pcie, 0x0, 0xC000C);
 }
 
-static int rk_pcie_link_up(struct dw_pcie *pci)
-{
-	struct rk_pcie *rk_pcie = to_rk_pcie(pci);
-	u32 val;
-
-	if (rk_pcie->is_rk1808) {
-		val = rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_GENERAL_DEBUG);
-		if ((val & (PCIE_PHY_LINKUP | PCIE_DATA_LINKUP)) == 0x3)
-			return 1;
-	} else {
-		val = rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_LTSSM_STATUS);
-		if ((val & (RDLH_LINKUP | SMLH_LINKUP)) == 0x30000)
-			return 1;
-	}
-
-	return 0;
-}
-
 static void rk_pcie_enable_debug(struct rk_pcie *rk_pcie)
 {
 	if (!IS_ENABLED(CONFIG_DEBUG_FS))
@@ -749,6 +744,8 @@
 	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;
+	int hw_retries = 0;
+	u32 ltssm;
 
 	/*
 	 * For standard RC, even if the link has been setup by firmware,
@@ -760,77 +757,92 @@
 		return 0;
 	}
 
-	/* Rest the device */
-	gpiod_set_value_cansleep(rk_pcie->rst_gpio, 0);
+	for (hw_retries = 0; hw_retries < RK_PCIE_ENUM_HW_RETRYIES; hw_retries++) {
+		/* Rest the device */
+		gpiod_set_value_cansleep(rk_pcie->rst_gpio, 0);
 
-	rk_pcie_disable_ltssm(rk_pcie);
-	rk_pcie_link_status_clear(rk_pcie);
-	rk_pcie_enable_debug(rk_pcie);
+		rk_pcie_disable_ltssm(rk_pcie);
+		rk_pcie_link_status_clear(rk_pcie);
+		rk_pcie_enable_debug(rk_pcie);
 
-	/* Enable client reset or link down interrupt */
-	rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_INTR_MASK, 0x40000);
+		/* Enable client reset or link down interrupt */
+		rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_INTR_MASK, 0x40000);
 
-	/* Enable LTSSM */
-	rk_pcie_enable_ltssm(rk_pcie);
+		/* Enable LTSSM */
+		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
-	 * Card Electromechanical Specification 3.0. So 100ms in total is the min
-	 * requuirement here. We add a 200ms by default for sake of hoping everthings
-	 * work fine. If it doesn't, please add more in DT node by add rockchip,perst-inactive-ms.
-	 */
-	msleep(rk_pcie->perst_inactive_ms);
-	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 < 100; retries++) {
-		if (dw_pcie_link_up(pci)) {
-			/*
-			 * We may be here in case of L0 in Gen1. But if EP is capable
-			 * of Gen2 or Gen3, Gen switch may happen just in this time, but
-			 * we keep on accessing devices in unstable link status. Given
-			 * that LTSSM max timeout is 24ms per period, we can wait a bit
-			 * more for Gen switch.
-			 */
-			msleep(50);
-			/* In case link drop after linkup, double check it */
-			if (dw_pcie_link_up(pci)) {
-				dev_info(pci->dev, "PCIe Link up, LTSSM is 0x%x\n",
-					 rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_LTSSM_STATUS));
-				rk_pcie_debug_dump(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;
 			}
 		}
 
-		dev_info_ratelimited(pci->dev, "PCIe Linking... LTSSM is 0x%x\n",
-				     rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_LTSSM_STATUS));
-		rk_pcie_debug_dump(rk_pcie);
-		msleep(20);
-	}
+		/*
+		 * 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
+		 * Card Electromechanical Specification 3.0. So 100ms in total is the min
+		 * requuirement here. We add a 200ms by default for sake of hoping everthings
+		 * work fine. If it doesn't, please add more in DT node by add rockchip,perst-inactive-ms.
+		 */
+		msleep(rk_pcie->perst_inactive_ms);
+		gpiod_set_value_cansleep(rk_pcie->rst_gpio, 1);
 
-	dev_err(pci->dev, "PCIe Link Fail\n");
+		/*
+		 * 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 < 100; retries++) {
+			if (dw_pcie_link_up(pci)) {
+				/*
+				 * We may be here in case of L0 in Gen1. But if EP is capable
+				 * of Gen2 or Gen3, Gen switch may happen just in this time, but
+				 * we keep on accessing devices in unstable link status. Given
+				 * that LTSSM max timeout is 24ms per period, we can wait a bit
+				 * more for Gen switch.
+				 */
+				msleep(50);
+				/* In case link drop after linkup, double check it */
+				if (dw_pcie_link_up(pci)) {
+					dev_info(pci->dev, "PCIe Link up, LTSSM is 0x%x\n",
+						rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_LTSSM_STATUS));
+					rk_pcie_debug_dump(rk_pcie);
+					return 0;
+				}
+			}
+
+			dev_info_ratelimited(pci->dev, "PCIe Linking... LTSSM is 0x%x\n",
+					rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_LTSSM_STATUS));
+			rk_pcie_debug_dump(rk_pcie);
+			msleep(20);
+		}
+
+		/*
+		 * In response to the situation where PCIe peripherals cannot be
+		 * enumerated due tosignal abnormalities, reset PERST# and reset
+		 * the peripheral power supply, then restart the enumeration.
+		 */
+		ltssm = rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_LTSSM_STATUS);
+		dev_err(pci->dev, "PCIe Link Fail, LTSSM is 0x%x, hw_retries=%d\n", ltssm, hw_retries);
+		if (ltssm >= 3 && !rk_pcie->is_signal_test) {
+			rk_pcie_disable_power(rk_pcie);
+			msleep(1000);
+			rk_pcie_enable_power(rk_pcie);
+		} else {
+			break;
+		}
+	}
 
 	return rk_pcie->is_signal_test == true ? 0 : -EINVAL;
 }
@@ -1119,6 +1131,10 @@
 
 	dw_pcie_setup_rc(pp);
 
+	/* Disable BAR0 BAR1 */
+	dw_pcie_writel_dbi2(pci, PCI_BASE_ADDRESS_0, 0x0);
+	dw_pcie_writel_dbi2(pci, PCI_BASE_ADDRESS_1, 0x0);
+
 	ret = rk_pcie_establish_link(pci);
 
 	if (pp->msi_irq > 0)
@@ -1190,7 +1206,6 @@
 		return ret;
 	}
 
-	rk_pcie->pci->dbi_base2 = rk_pcie->pci->dbi_base + PCIE_TYPE0_HDR_DBI2_OFFSET;
 	rk_pcie->pci->atu_base = rk_pcie->pci->dbi_base + DEFAULT_DBI_ATU_OFFSET;
 	rk_pcie->pci->iatu_unroll_enabled = rk_pcie_iatu_unroll_enabled(rk_pcie->pci);
 
@@ -1250,6 +1265,7 @@
 		return PTR_ERR(rk_pcie->dbi_base);
 
 	rk_pcie->pci->dbi_base = rk_pcie->dbi_base;
+	rk_pcie->pci->dbi_base2 = rk_pcie->pci->dbi_base + PCIE_TYPE0_HDR_DBI2_OFFSET;
 
 	apb_base = platform_get_resource_byname(pdev, IORESOURCE_MEM,
 						"pcie-apb");
@@ -1434,13 +1450,37 @@
 	table->start.chnl = table->chn;
 }
 
+static void rk_pcie_hot_rst_work(struct work_struct *work)
+{
+	struct rk_pcie *rk_pcie = container_of(work, struct rk_pcie, hot_rst_work);
+	u32 val, status;
+	int ret;
+
+	/* Setup command register */
+	val = dw_pcie_readl_dbi(rk_pcie->pci, PCI_COMMAND);
+	val &= 0xffff0000;
+	val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+		PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
+	dw_pcie_writel_dbi(rk_pcie->pci, PCI_COMMAND, val);
+
+	if (rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_HOT_RESET_CTRL) & PCIE_LTSSM_APP_DLY2_EN) {
+		ret = readl_poll_timeout(rk_pcie->apb_base + PCIE_CLIENT_LTSSM_STATUS,
+			 status, ((status & 0x3F) == 0), 100, RK_PCIE_HOTRESET_TMOUT_US);
+		if (ret)
+			dev_err(rk_pcie->pci->dev, "wait for detect quiet failed!\n");
+
+		rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_HOT_RESET_CTRL,
+			(PCIE_LTSSM_APP_DLY2_DONE) | ((PCIE_LTSSM_APP_DLY2_DONE) << 16));
+	}
+}
+
 static irqreturn_t rk_pcie_sys_irq_handler(int irq, void *arg)
 {
 	struct rk_pcie *rk_pcie = arg;
 	u32 chn;
 	union int_status status;
 	union int_clear clears;
-	u32 reg, val;
+	u32 reg;
 
 	status.asdword = dw_pcie_readl_dbi(rk_pcie->pci, PCIE_DMA_OFFSET +
 					   PCIE_DMA_WR_INT_STATUS);
@@ -1481,14 +1521,8 @@
 	}
 
 	reg = rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_INTR_STATUS_MISC);
-	if (reg & BIT(2)) {
-		/* Setup command register */
-		val = dw_pcie_readl_dbi(rk_pcie->pci, PCI_COMMAND);
-		val &= 0xffff0000;
-		val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
-		       PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
-		dw_pcie_writel_dbi(rk_pcie->pci, PCI_COMMAND, val);
-	}
+	if (reg & BIT(2))
+		queue_work(rk_pcie->hot_rst_wq, &rk_pcie->hot_rst_work);
 
 	rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_INTR_STATUS_MISC, reg);
 
@@ -1570,7 +1604,6 @@
 
 static const struct dw_pcie_ops dw_pcie_ops = {
 	.start_link = rk_pcie_establish_link,
-	.link_up = rk_pcie_link_up,
 };
 
 static int rk1808_pcie_fixup(struct rk_pcie *rk_pcie, struct device_node *np)
@@ -1612,7 +1645,8 @@
 
 	/* LTSSM EN ctrl mode */
 	val = rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_HOT_RESET_CTRL);
-	val |= PCIE_LTSSM_ENABLE_ENHANCE | (PCIE_LTSSM_ENABLE_ENHANCE << 16);
+	val |= (PCIE_LTSSM_ENABLE_ENHANCE | PCIE_LTSSM_APP_DLY2_EN)
+		| ((PCIE_LTSSM_APP_DLY2_EN | PCIE_LTSSM_ENABLE_ENHANCE) << 16);
 	rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_HOT_RESET_CTRL, val);
 }
 
@@ -1650,7 +1684,7 @@
 static int rk_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
 			    irq_hw_number_t hwirq)
 {
-	irq_set_chip_and_handler(irq, &rk_pcie_legacy_irq_chip, handle_simple_irq);
+	irq_set_chip_and_handler(irq, &rk_pcie_legacy_irq_chip, handle_level_irq);
 	irq_set_chip_data(irq, domain->host_data);
 
 	return 0;
@@ -1956,6 +1990,7 @@
 
 	if (!IS_ERR_OR_NULL(rk_pcie->prsnt_gpio)) {
 		if (!gpiod_get_value(rk_pcie->prsnt_gpio)) {
+			dev_info(dev, "device isn't present\n");
 			ret = -ENODEV;
 			goto release_driver;
 		}
@@ -2051,17 +2086,39 @@
 		rk_pcie->is_signal_test = true;
 	}
 
-	/* Force into compliance mode */
-	if (device_property_read_bool(dev, "rockchip,compliance-mode")) {
-		val = dw_pcie_readl_dbi(pci, PCIE_CAP_LINK_CONTROL2_LINK_STATUS);
-		val |= BIT(4);
-		dw_pcie_writel_dbi(pci, PCIE_CAP_LINK_CONTROL2_LINK_STATUS, val);
+	/*
+	 * Force into compliance mode
+	 * comp_prst is a two dimensional array of which the first element
+	 * stands for speed mode, and the second one is preset value encoding:
+	 * [0] 0->SMA tool control the signal switch, 1/2/3 is for manual Gen setting
+	 * [1] transmitter setting for manual Gen setting, valid only if [0] isn't zero.
+	 */
+	if (!device_property_read_u32_array(dev, "rockchip,compliance-mode",
+					    rk_pcie->comp_prst, 2)) {
+		BUG_ON(rk_pcie->comp_prst[0] > 3 || rk_pcie->comp_prst[1] > 10);
+		if (!rk_pcie->comp_prst[0]) {
+			dev_info(dev, "Auto compliance mode for SMA tool.\n");
+		} else {
+			dev_info(dev, "compliance mode for soldered board Gen%d, P%d.\n",
+				 rk_pcie->comp_prst[0], rk_pcie->comp_prst[1]);
+			val = dw_pcie_readl_dbi(pci, PCIE_CAP_LINK_CONTROL2_LINK_STATUS);
+			val |= BIT(4) | rk_pcie->comp_prst[0] | (rk_pcie->comp_prst[1] << 12);
+			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;
+
+	rk_pcie->hot_rst_wq = create_singlethread_workqueue("rk_pcie_hot_rst_wq");
+	if (!rk_pcie->hot_rst_wq) {
+		dev_err(dev, "failed to create hot_rst workqueue\n");
+		ret = -ENOMEM;
+		goto remove_irq_domain;
+	}
+	INIT_WORK(&rk_pcie->hot_rst_work, rk_pcie_hot_rst_work);
 
 	switch (rk_pcie->mode) {
 	case RK_PCIE_RC_TYPE:
@@ -2076,12 +2133,12 @@
 		return 0;
 
 	if (ret)
-		goto remove_irq_domain;
+		goto remove_rst_wq;
 
 	ret = rk_pcie_init_dma_trx(rk_pcie);
 	if (ret) {
 		dev_err(dev, "failed to add dma extension\n");
-		goto remove_irq_domain;
+		goto remove_rst_wq;
 	}
 
 	if (rk_pcie->dma_obj) {
@@ -2093,7 +2150,7 @@
 		/* hold link reset grant after link-up */
 		ret = rk_pcie_reset_grant_ctrl(rk_pcie, false);
 		if (ret)
-			goto remove_irq_domain;
+			goto remove_rst_wq;
 	}
 
 	dw_pcie_dbi_ro_wr_dis(pci);
@@ -2121,6 +2178,8 @@
 
 	return 0;
 
+remove_rst_wq:
+	destroy_workqueue(rk_pcie->hot_rst_wq);
 remove_irq_domain:
 	if (rk_pcie->irq_domain)
 		irq_domain_remove(rk_pcie->irq_domain);
@@ -2298,11 +2357,19 @@
 no_l2:
 	rk_pcie_disable_ltssm(rk_pcie);
 
+	ret = phy_validate(rk_pcie->phy, PHY_TYPE_PCIE, 0, NULL);
+	if (ret && ret != -EOPNOTSUPP) {
+		dev_err(dev, "PHY is reused by other controller, check the dts!\n");
+		return ret;
+	}
+
 	/* make sure assert phy success */
 	usleep_range(200, 300);
 
 	phy_power_off(rk_pcie->phy);
 	phy_exit(rk_pcie->phy);
+
+	rk_pcie->intx = rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_INTR_MASK_LEGACY);
 
 	clk_bulk_disable_unprepare(rk_pcie->clk_cnt, rk_pcie->clks);
 
@@ -2368,6 +2435,9 @@
 	if (std_rc)
 		dw_pcie_setup_rc(&rk_pcie->pci->pp);
 
+	rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_INTR_MASK_LEGACY,
+			   rk_pcie->intx | 0xffff0000);
+
 	ret = rk_pcie_establish_link(rk_pcie->pci);
 	if (ret) {
 		dev_err(dev, "failed to establish pcie link\n");

--
Gitblit v1.6.2