]> git.itanic.dy.fi Git - linux-stable/commitdiff
PCI: cadence: Add quirk flag to set minimum delay in LTSSM Detect.Quiet state
authorNadeem Athani <nadeem@cadence.com>
Wed, 11 Aug 2021 12:33:33 +0000 (18:03 +0530)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 22 Sep 2021 10:39:26 +0000 (12:39 +0200)
[ Upstream commit 09c24094b2e3a15ef3fc44f54a191b3db522fb11 ]

PCIe fails to link up if SERDES lanes not used by PCIe are assigned to
another protocol. For example, link training fails if lanes 2 and 3 are
assigned to another protocol while lanes 0 and 1 are used for PCIe to
form a two lane link. This failure is due to an incorrect tie-off on an
internal status signal indicating electrical idle.

Status signals going from SERDES to PCIe Controller are tied-off when a
lane is not assigned to PCIe. Signal indicating electrical idle is
incorrectly tied-off to a state that indicates non-idle. As a result,
PCIe sees unused lanes to be out of electrical idle and this causes
LTSSM to exit Detect.Quiet state without waiting for 12ms timeout to
occur. If a receiver is not detected on the first receiver detection
attempt in Detect.Active state, LTSSM goes back to Detect.Quiet and
again moves forward to Detect.Active state without waiting for 12ms as
required by PCIe base specification. Since wait time in Detect.Quiet is
skipped, multiple receiver detect operations are performed back-to-back
without allowing time for capacitance on the transmit lines to
discharge. This causes subsequent receiver detection to always fail even
if a receiver gets connected eventually.

Add a quirk flag "quirk_detect_quiet_flag" to program the minimum
time the LTSSM should wait on entering Detect.Quiet state here.
This has to be set for J7200 as it has an incorrect tie-off on unused
lanes.

Link: https://lore.kernel.org/r/20210811123336.31357-3-kishon@ti.com
Signed-off-by: Nadeem Athani <nadeem@cadence.com>
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/pci/controller/cadence/pcie-cadence-ep.c
drivers/pci/controller/cadence/pcie-cadence-host.c
drivers/pci/controller/cadence/pcie-cadence.c
drivers/pci/controller/cadence/pcie-cadence.h

index 897cdde02bd80abb0bd1876ff1785e7306ffc0cd..dd7df1ac7fda2417e1fd165bd1707d0d4081e7c1 100644 (file)
@@ -623,6 +623,10 @@ int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep)
        ep->irq_pci_addr = CDNS_PCIE_EP_IRQ_PCI_ADDR_NONE;
        /* Reserve region 0 for IRQs */
        set_bit(0, &ep->ob_region_map);
+
+       if (ep->quirk_detect_quiet_flag)
+               cdns_pcie_detect_quiet_min_delay_set(&ep->pcie);
+
        spin_lock_init(&ep->lock);
 
        return 0;
index ae1c55503513a910af620620c70997e2913d71c6..fb96d37a135c1b0ce45f74e5db56739b0af5d961 100644 (file)
@@ -498,6 +498,9 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc)
                return PTR_ERR(rc->cfg_base);
        rc->cfg_res = res;
 
+       if (rc->quirk_detect_quiet_flag)
+               cdns_pcie_detect_quiet_min_delay_set(&rc->pcie);
+
        ret = cdns_pcie_start_link(pcie);
        if (ret) {
                dev_err(dev, "Failed to start link\n");
index 3c3646502d05cf12cfa802e6f58499bea0bd0ae5..52767f26048fd787ca9f1942e852d9534bec214b 100644 (file)
@@ -7,6 +7,22 @@
 
 #include "pcie-cadence.h"
 
+void cdns_pcie_detect_quiet_min_delay_set(struct cdns_pcie *pcie)
+{
+       u32 delay = 0x3;
+       u32 ltssm_control_cap;
+
+       /*
+        * Set the LTSSM Detect Quiet state min. delay to 2ms.
+        */
+       ltssm_control_cap = cdns_pcie_readl(pcie, CDNS_PCIE_LTSSM_CONTROL_CAP);
+       ltssm_control_cap = ((ltssm_control_cap &
+                           ~CDNS_PCIE_DETECT_QUIET_MIN_DELAY_MASK) |
+                           CDNS_PCIE_DETECT_QUIET_MIN_DELAY(delay));
+
+       cdns_pcie_writel(pcie, CDNS_PCIE_LTSSM_CONTROL_CAP, ltssm_control_cap);
+}
+
 void cdns_pcie_set_outbound_region(struct cdns_pcie *pcie, u8 busnr, u8 fn,
                                   u32 r, bool is_io,
                                   u64 cpu_addr, u64 pci_addr, size_t size)
index bc27d126f2394f817204a1c4985615db93cbdc07..4bde99b74135df7371a9179d43e4be005d8243bf 100644 (file)
 /* AXI link down register */
 #define CDNS_PCIE_AT_LINKDOWN (CDNS_PCIE_AT_BASE + 0x0824)
 
+/* LTSSM Capabilities register */
+#define CDNS_PCIE_LTSSM_CONTROL_CAP             (CDNS_PCIE_LM_BASE + 0x0054)
+#define  CDNS_PCIE_DETECT_QUIET_MIN_DELAY_MASK  GENMASK(2, 1)
+#define  CDNS_PCIE_DETECT_QUIET_MIN_DELAY_SHIFT 1
+#define  CDNS_PCIE_DETECT_QUIET_MIN_DELAY(delay) \
+        (((delay) << CDNS_PCIE_DETECT_QUIET_MIN_DELAY_SHIFT) & \
+        CDNS_PCIE_DETECT_QUIET_MIN_DELAY_MASK)
+
 enum cdns_pcie_rp_bar {
        RP_BAR_UNDEFINED = -1,
        RP_BAR0,
@@ -295,6 +303,7 @@ struct cdns_pcie {
  * @avail_ib_bar: Satus of RP_BAR0, RP_BAR1 and        RP_NO_BAR if it's free or
  *                available
  * @quirk_retrain_flag: Retrain link as quirk for PCIe Gen2
+ * @quirk_detect_quiet_flag: LTSSM Detect Quiet min delay set as quirk
  */
 struct cdns_pcie_rc {
        struct cdns_pcie        pcie;
@@ -304,6 +313,7 @@ struct cdns_pcie_rc {
        u32                     device_id;
        bool                    avail_ib_bar[CDNS_PCIE_RP_MAX_IB];
        unsigned int            quirk_retrain_flag:1;
+       unsigned int            quirk_detect_quiet_flag:1;
 };
 
 /**
@@ -334,6 +344,7 @@ struct cdns_pcie_epf {
  *        registers fields (RMW) accessible by both remote RC and EP to
  *        minimize time between read and write
  * @epf: Structure to hold info about endpoint function
+ * @quirk_detect_quiet_flag: LTSSM Detect Quiet min delay set as quirk
  */
 struct cdns_pcie_ep {
        struct cdns_pcie        pcie;
@@ -348,6 +359,7 @@ struct cdns_pcie_ep {
        /* protect writing to PCI_STATUS while raising legacy interrupts */
        spinlock_t              lock;
        struct cdns_pcie_epf    *epf;
+       unsigned int            quirk_detect_quiet_flag:1;
 };
 
 
@@ -508,6 +520,9 @@ static inline int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep)
        return 0;
 }
 #endif
+
+void cdns_pcie_detect_quiet_min_delay_set(struct cdns_pcie *pcie);
+
 void cdns_pcie_set_outbound_region(struct cdns_pcie *pcie, u8 busnr, u8 fn,
                                   u32 r, bool is_io,
                                   u64 cpu_addr, u64 pci_addr, size_t size);