From 4b0c6453808a662869a43c504913f3b7ed64486a Mon Sep 17 00:00:00 2001
From: Jonathan Bell <jonathan@raspberrypi.com>
Date: Wed, 20 Sep 2023 13:01:11 +0100
Subject: [PATCH] drivers: pci: brcmstb: optionally extend Tperst_clk time
 during link-up

The RC has a feature that allows for manual control over the deassertion
of the PERST# output pin, which allows the time between refclk active
and reset deassert at the EP to be increased.

Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
---
 drivers/pci/controller/pcie-brcmstb.c | 24 +++++++++++++++++++++++-
 1 file changed, 23 insertions(+), 1 deletion(-)

--- a/drivers/pci/controller/pcie-brcmstb.c
+++ b/drivers/pci/controller/pcie-brcmstb.c
@@ -138,6 +138,7 @@
 
 #define PCIE_MISC_HARD_PCIE_HARD_DEBUG	pcie->reg_offsets[PCIE_HARD_DEBUG]
 #define  PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK	0x2
+#define  PCIE_MISC_HARD_PCIE_HARD_DEBUG_PERST_ASSERT_MASK		0x8
 #define  PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK		0x08000000
 #define  PCIE_BMIPS_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK		0x00800000
 #define  PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_L1SS_ENABLE_MASK		0x00200000
@@ -352,6 +353,7 @@ struct brcm_pcie {
 	bool			(*rc_mode)(struct brcm_pcie *pcie);
 	struct subdev_regulators *sr;
 	bool			ep_wakeup_capable;
+	u32			tperst_clk_ms;
 };
 
 static inline bool is_bmips(const struct brcm_pcie *pcie)
@@ -1388,9 +1390,28 @@ static int brcm_pcie_start_link(struct b
 	u16 nlw, cls, lnksta;
 	bool ssc_good = false;
 	int ret, i;
+	u32 tmp;
 
 	/* Unassert the fundamental reset */
-	pcie->perst_set(pcie, 0);
+	if (pcie->tperst_clk_ms) {
+		/*
+		 * Increase Tperst_clk time by forcing PERST# output low while
+		 * the internal reset is released, so the PLL generates stable
+		 * refclk output further in advance of PERST# deassertion.
+		 */
+		tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
+		u32p_replace_bits(&tmp, 1, PCIE_MISC_HARD_PCIE_HARD_DEBUG_PERST_ASSERT_MASK);
+		writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
+
+		pcie->perst_set(pcie, 0);
+		msleep(pcie->tperst_clk_ms);
+
+		tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
+		u32p_replace_bits(&tmp, 0, PCIE_MISC_HARD_PCIE_HARD_DEBUG_PERST_ASSERT_MASK);
+		writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
+	} else {
+		pcie->perst_set(pcie, 0);
+	}
 
 	/*
 	 * Wait for 100ms after PERST# deassertion; see PCIe CEM specification
@@ -1923,6 +1944,7 @@ static int brcm_pcie_probe(struct platfo
 	pcie->ssc = of_property_read_bool(np, "brcm,enable-ssc");
 	pcie->l1ss = of_property_read_bool(np, "brcm,enable-l1ss");
 	pcie->rcb_mps_mode = of_property_read_bool(np, "brcm,enable-mps-rcb");
+	of_property_read_u32(np, "brcm,tperst-clk-ms", &pcie->tperst_clk_ms);
 
 	ret = clk_prepare_enable(pcie->clk);
 	if (ret) {
