diff mbox series

[Linaro-uefi,Linaro-uefi,v3,3/3] Hisilicon: Add reconfig lane number feature

Message ID 1491827595-84884-4-git-send-email-chenhui.sun@linaro.org
State New
Headers show
Series D03/D05 platforms bug fix | expand

Commit Message

Chenhui Sun April 10, 2017, 12:33 p.m. UTC
In some cases, the PCIe device may close part of lanes in
config state of LTSSM, the hip06 RC should reconfig lane number
and try to linkup again.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Chenhui Sun <sunchenhui@huawei.com>
Signed-off-by: Heyi Guo <heyi.guo@linaro.org>
Signed-off-by: Yi Li <phoenix.liyi@huawei.com>
Reviewed-by: Leif Lindholm <leif.lindholm@linaro.org>
---
 .../Hi1610/Drivers/PcieInit1610/PcieInitLib.c      | 136 ++++++++++++++++++++-
 Chips/Hisilicon/Include/Regs/HisiPcieV1RegOffset.h |   5 +
 2 files changed, 139 insertions(+), 2 deletions(-)
diff mbox series

Patch

diff --git a/Chips/Hisilicon/Hi1610/Drivers/PcieInit1610/PcieInitLib.c b/Chips/Hisilicon/Hi1610/Drivers/PcieInit1610/PcieInitLib.c
index 1df7a90..8ab7fa3 100644
--- a/Chips/Hisilicon/Hi1610/Drivers/PcieInit1610/PcieInitLib.c
+++ b/Chips/Hisilicon/Hi1610/Drivers/PcieInit1610/PcieInitLib.c
@@ -39,6 +39,14 @@  extern PCIE_DRIVER_CFG gastr_pcie_driver_cfg;
 extern PCIE_IATU gastr_pcie_iatu_cfg;
 extern PCIE_IATU_VA mPcieIatuTable;
 
+EFI_STATUS
+EFIAPI
+PciePortInit (
+  IN UINT32 soctype,
+  IN UINT32 HostBridgeNum,
+  IN PCIE_DRIVER_CFG *PcieCfg
+  );
+
 VOID PcieRegWrite(UINT32 Port, UINTN Offset, UINT32 Value)
 {
     RegWrite((UINT64)mPcieIntCfg.RegResource[Port] + Offset, Value);
@@ -149,8 +157,131 @@  VOID PcieRxValidCtrl(UINT32 soctype, UINT32 HostBridgeNum, UINT32 Port, BOOLEAN
         }
     }
 }
+/*
+ * The ltssm register is assigned in an asynchronous way, the value
+ * of register may not right in metastable state.
+ * Read the register twice to get stable value.
+ */
+VOID PcieGetLtssmValue (
+  IN UINT32 HostBridgeNum,
+  IN UINT32 Port,
+  IN UINT32 *Value
+  )
+{
+  UINT32  ValueA;
+  UINT32  ValueB = 0;
+  UINT32  Count;
+
+  RegRead (PCIE_APB_SLAVE_BASE_1610[HostBridgeNum][Port] + PCIE_SYS_REG_OFFSET + PCIE_SYS_STATE4_REG, ValueA);
+  ValueA = ValueA & PCIE_LTSSM_STATE_MASK;
+
+  Count = 0;
+  while (Count < 2) {
+
+    RegRead (PCIE_APB_SLAVE_BASE_1610[HostBridgeNum][Port] + PCIE_SYS_REG_OFFSET + PCIE_SYS_STATE4_REG, ValueB);
+    ValueB = ValueB & PCIE_LTSSM_STATE_MASK;
+
+    /* Get the same state in continuous two times*/
+    if (ValueA == ValueB) {
+      break;
+    }
+
+    //If the second value not equal to the first, we return the second one as the stable
+    ValueA = ValueB;
+    Count++;
+  }
+
+  *Value = ValueB;
+
+  return;
+
+}
+
+/*
+ * In some cases, the PCIe device may close part of lanes in
+ * config state of LTSSM, the hip06 RC should reconfig lane num
+ * and try to linkup again.
+ */
+VOID PcieReconfigLaneNum (
+  IN UINT32 soctype,
+  IN UINT32 HostBridgeNum,
+  IN UINT32 Port,
+  IN PCIE_DRIVER_CFG *PcieCfg
+  )
+{
+  EFI_STATUS Status;
+  UINT32  LtssmStatus;
+  UINT32  RegVal;
+  UINT32  LoopCnt = 0;
+  UINT32  LaneNumCnt = 0;
+  PCIE_PORT_WIDTH PortWidth = PcieCfg->PortInfo.PortWidth;
+
+  // 500 * 200us = 100ms, so it takes 100 ms must to reconfig lane numbers
+  while (LoopCnt < 500) {
+
+    /*
+     * The minimum lanenum is 1, no need to try any more.
+     */
+    if (PortWidth <= 1) {
+      DEBUG ((DEBUG_ERROR, "PcieReconfigLanenum  PortWidth <= 1 !\n"));
+      return;
+    }
+
+    /*
+     * Check the lane num config state is normal or not.
+     */
+    PcieGetLtssmValue (HostBridgeNum, Port, &LtssmStatus);
+    if ((LtssmStatus == PCIE_LTSSM_CFG_LANENUM_ACPT) || (LtssmStatus == PCIE_LTSSM_CFG_COMPLETE)) {
+      LaneNumCnt++;
+    } else if (LtssmStatus == PCIE_LTSSM_LINKUP_STATE) {
+      PcieGetLtssmValue (HostBridgeNum, Port, &LtssmStatus);
+      if (LtssmStatus == PCIE_LTSSM_LINKUP_STATE) {
+          break;
+      }
+    } else {
+      LaneNumCnt = 0;
+    }
+
+    /*
+     * The lane num config state is abnormal, need to reconfig
+     * the lane num and try to establish link again.
+     */
+    if (LaneNumCnt > MAX_TRY_LINK_NUM) {
+      /* Disable LTSSM */
+      RegRead (PCIE_APB_SLAVE_BASE_1610[HostBridgeNum][Port] + PCIE_CTRL_7_REG, RegVal);
+      RegVal &= ~(LTSSM_ENABLE);
+      RegWrite (PCIE_APB_SLAVE_BASE_1610[HostBridgeNum][Port] + PCIE_CTRL_7_REG, RegVal);
+      /*
+       * Decrease the PortWidth and try to link again,
+       * the value of PortWidth 0xf (X8), 0x7(x4), 0x3(X2), 0x1(X1)
+       */
+      PcieCfg->PortInfo.PortWidth = (PCIE_PORT_WIDTH)((UINT8)PcieCfg->PortInfo.PortWidth >> 1);
+
+      Status = PciePortInit (soctype, HostBridgeNum, PcieCfg);
+      if (EFI_ERROR(Status)) {
+          DEBUG ((DEBUG_ERROR, "PcieReconfigLanenum HostBridge %d, Pcie Port %d Init Failed! \n", HostBridgeNum, Port));
+      }
+      return;
+    }
+
+    LoopCnt++;
+    /* Pcie 3.0 Spec,part 4.2.6.3.4.1: the Upstream Lanes are permitted
+     * delay up to 1 ms before transitioning to Configuration.Lanenum.Accept.
+     * So the delay time 200 us * 5(LanNumCnt) = 1ms, not beyond the reasonable range.
+     */
+    MicroSecondDelay (200);
+  }
+
+  return ;
+}
 
-EFI_STATUS PcieEnableItssm(UINT32 soctype, UINT32 HostBridgeNum, UINT32 Port)
+EFI_STATUS
+PcieEnableItssm (
+  IN UINT32 soctype,
+  IN UINT32 HostBridgeNum,
+  IN UINT32 Port,
+  IN PCIE_DRIVER_CFG *PcieCfg
+  )
 {
     PCIE_CTRL_7_U pcie_ctrl7;
     UINT32 Value = 0;
@@ -165,6 +296,7 @@  EFI_STATUS PcieEnableItssm(UINT32 soctype, UINT32 HostBridgeNum, UINT32 Port)
         Value |= BIT11|BIT30|BIT31;
         RegWrite(PCIE_APB_SLAVE_BASE_1610[HostBridgeNum][Port] + 0x1114, Value);
         (VOID)PcieRxValidCtrl(soctype, HostBridgeNum, Port, 1);
+        PcieReconfigLaneNum (soctype, HostBridgeNum, Port, PcieCfg);
         return EFI_SUCCESS;
     }
     else
@@ -1008,7 +1140,7 @@  PciePortInit (
      /* Disable RC Option Rom */
      DisableRcOptionRom (soctype, HostBridgeNum, PortIndex, PcieCfg->PortInfo.PortType);
      /* assert LTSSM enable */
-     (VOID)PcieEnableItssm(soctype, HostBridgeNum, PortIndex);
+     (VOID)PcieEnableItssm (soctype, HostBridgeNum, PortIndex, PcieCfg);
      if (FeaturePcdGet(PcdIsPciPerfTuningEnable)) {
        //PCIe will still work even if performance tuning fails,
        //and there is warning message inside the function to print
diff --git a/Chips/Hisilicon/Include/Regs/HisiPcieV1RegOffset.h b/Chips/Hisilicon/Include/Regs/HisiPcieV1RegOffset.h
index 539d567..bf57652 100644
--- a/Chips/Hisilicon/Include/Regs/HisiPcieV1RegOffset.h
+++ b/Chips/Hisilicon/Include/Regs/HisiPcieV1RegOffset.h
@@ -8982,6 +8982,7 @@  typedef union tagIepMsiCtrlIntStatus
 #define PCIE_SYS_CTRL28_REG  (PCI_SYS_BASE + 0x1c4)
 #define PCIE_SYS_CTRL29_REG  (PCI_SYS_BASE + 0x1c8)
 #define PCIE_SYS_CTRL54_REG  (PCI_SYS_BASE + 0x274)
+#define PCIE_SYS_STATE4_REG  (PCI_SYS_BASE + 0x31C)
 #define PCIE_SYS_STATE5_REG  (PCI_SYS_BASE + 0x30)
 #define PCIE_SYS_STATE6_REG  (PCI_SYS_BASE + 0x34)
 #define PCIE_SYS_STATE7_REG  (PCI_SYS_BASE + 0x38)
@@ -12694,7 +12695,11 @@  typedef union tagPortlogic93
 #define PCIE_SUBCTRL_SC_PCIE0_SYS_STATE3_REG               (PCIE_SUBCTRL_BASE + 0x6814)
 #define PCIE_SUBCTRL_SC_PCIE0_SYS_STATE4_REG               (PCIE_SUBCTRL_BASE + 0x6818)
 #define PCIE_LTSSM_STATE_MASK  (0x3f)
+#define PCIE_LTSSM_CFG_LANENUM_ACPT                         0x0a
+#define PCIE_LTSSM_CFG_COMPLETE                             0x0b
 #define PCIE_LTSSM_LINKUP_STATE  (0x11)
+#define LTSSM_ENABLE  BIT11
+#define MAX_TRY_LINK_NUM  5
 #define PCIE_SUBCTRL_SC_PCIE0_AXI_MSTR_OOO_WR_STS0_REG     (PCIE_SUBCTRL_BASE + 0x6880)
 #define PCIE_SUBCTRL_SC_PCIE0_AXI_MSTR_OOO_WR_STS1_REG     (PCIE_SUBCTRL_BASE + 0x6884)
 #define PCIE_SUBCTRL_SC_PCIE0_AXI_MSTR_OOO_RD_STS0_REG     (PCIE_SUBCTRL_BASE + 0x6890)