Rockchip platforms provide a 64x4 bytes debug FIFO to trace the
LTSSM history. Any LTSSM change will be recorded. It's userful
for debug purpose, for example link failure, etc.

Signed-off-by: Shawn Lin <[email protected]>
---

Changes in v2:
- use tracepoint

 drivers/pci/controller/dwc/pcie-dw-rockchip.c | 92 +++++++++++++++++++++++++++
 1 file changed, 92 insertions(+)

diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c 
b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
index 352f513..be9639aa 100644
--- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c
+++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
@@ -22,6 +22,8 @@
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/reset.h>
+#include <linux/workqueue.h>
+#include <trace/events/pci_controller.h>
 
 #include "../../pci.h"
 #include "pcie-designware.h"
@@ -73,6 +75,18 @@
 #define  PCIE_CLIENT_CDM_RASDES_TBA_L1_1       BIT(4)
 #define  PCIE_CLIENT_CDM_RASDES_TBA_L1_2       BIT(5)
 
+/* Debug FIFO information */
+#define PCIE_CLIENT_DBG_FIFO_MODE_CON  0x310
+#define  PCIE_CLIENT_DBG_EN            0xffff0007
+#define  PCIE_CLIENT_DBG_DIS           0xffff0000
+#define PCIE_CLIENT_DBG_FIFO_PTN_HIT_D0        0x320
+#define PCIE_CLIENT_DBG_FIFO_PTN_HIT_D1        0x324
+#define PCIE_CLIENT_DBG_FIFO_TRN_HIT_D0        0x328
+#define PCIE_CLIENT_DBG_FIFO_TRN_HIT_D1        0x32c
+#define  PCIE_CLIENT_DBG_TRANSITION_DATA 0xffff0000
+#define PCIE_CLIENT_DBG_FIFO_STATUS    0x350
+#define PCIE_DBG_LTSSM_HISTORY_CNT     64
+
 /* Hot Reset Control Register */
 #define PCIE_CLIENT_HOT_RESET_CTRL     0x180
 #define  PCIE_LTSSM_APP_DLY2_EN                BIT(1)
@@ -96,6 +110,7 @@ struct rockchip_pcie {
        struct irq_domain *irq_domain;
        const struct rockchip_pcie_of_data *data;
        bool supports_clkreq;
+       struct delayed_work trace_work;
 };
 
 struct rockchip_pcie_of_data {
@@ -206,6 +221,79 @@ static enum dw_pcie_ltssm rockchip_pcie_get_ltssm(struct 
dw_pcie *pci)
        return rockchip_pcie_get_ltssm_reg(rockchip) & PCIE_LTSSM_STATUS_MASK;
 }
 
+#ifdef CONFIG_TRACING
+static void rockchip_pcie_ltssm_trace_work(struct work_struct *work)
+{
+       struct rockchip_pcie *rockchip = container_of(work, struct 
rockchip_pcie,
+                                               trace_work.work);
+       struct dw_pcie *pci = &rockchip->pci;
+       enum dw_pcie_ltssm state;
+       u32 val, rate, l1ss, loop, prev_val = DW_PCIE_LTSSM_UNKNOWN;
+
+       for (loop = 0; loop < PCIE_DBG_LTSSM_HISTORY_CNT; loop++) {
+               val = rockchip_pcie_readl_apb(rockchip, 
PCIE_CLIENT_DBG_FIFO_STATUS);
+               rate = (val & GENMASK(22, 20)) >> 20;
+               l1ss = (val & GENMASK(10, 8)) >> 8;
+               val &= PCIE_LTSSM_STATUS_MASK;
+
+               /* Two consecutive identical LTSSM means invalid subsequent 
data */
+               if ((loop > 0 && val == prev_val) || val > 
DW_PCIE_LTSSM_RCVRY_EQ3)
+                       break;
+
+               state = prev_val = val;
+               if (val == DW_PCIE_LTSSM_L1_IDLE) {
+                       if (l1ss == 2)
+                               state = DW_PCIE_LTSSM_L1_2;
+                       else if (l1ss == 1)
+                               state = DW_PCIE_LTSSM_L1_1;
+               }
+
+               trace_pcie_ltssm_state_transition(dev_name(pci->dev),
+                                       dw_pcie_ltssm_status_string(state),
+                                       ((rate + 1) > pci->max_link_speed) ?
+                                       PCI_SPEED_UNKNOWN : PCIE_SPEED_2_5GT + 
rate);
+       }
+
+       schedule_delayed_work(&rockchip->trace_work, msecs_to_jiffies(5000));
+}
+
+static void rockchip_pcie_ltssm_trace(struct rockchip_pcie *rockchip,
+                                     bool en)
+{
+       if (en) {
+               rockchip_pcie_writel_apb(rockchip,
+                                        PCIE_CLIENT_DBG_TRANSITION_DATA,
+                                        PCIE_CLIENT_DBG_FIFO_PTN_HIT_D0);
+               rockchip_pcie_writel_apb(rockchip,
+                                        PCIE_CLIENT_DBG_TRANSITION_DATA,
+                                        PCIE_CLIENT_DBG_FIFO_PTN_HIT_D1);
+               rockchip_pcie_writel_apb(rockchip,
+                                        PCIE_CLIENT_DBG_TRANSITION_DATA,
+                                        PCIE_CLIENT_DBG_FIFO_TRN_HIT_D0);
+               rockchip_pcie_writel_apb(rockchip,
+                                        PCIE_CLIENT_DBG_TRANSITION_DATA,
+                                        PCIE_CLIENT_DBG_FIFO_TRN_HIT_D1);
+               rockchip_pcie_writel_apb(rockchip,
+                                        PCIE_CLIENT_DBG_EN,
+                                        PCIE_CLIENT_DBG_FIFO_MODE_CON);
+
+               INIT_DELAYED_WORK(&rockchip->trace_work,
+                                 rockchip_pcie_ltssm_trace_work);
+               schedule_delayed_work(&rockchip->trace_work, 0);
+       } else {
+               rockchip_pcie_writel_apb(rockchip,
+                                        PCIE_CLIENT_DBG_DIS,
+                                        PCIE_CLIENT_DBG_FIFO_MODE_CON);
+               cancel_delayed_work_sync(&rockchip->trace_work);
+       }
+}
+#else
+static void rockchip_pcie_ltssm_trace(struct rockchip_pcie *rockchip,
+                                     bool en)
+{
+}
+#endif
+
 static void rockchip_pcie_enable_ltssm(struct rockchip_pcie *rockchip)
 {
        rockchip_pcie_writel_apb(rockchip, PCIE_CLIENT_ENABLE_LTSSM,
@@ -289,6 +377,9 @@ static int rockchip_pcie_start_link(struct dw_pcie *pci)
         * 100us as we don't know how long should the device need to reset.
         */
        msleep(PCIE_T_PVPERL_MS);
+
+       rockchip_pcie_ltssm_trace(rockchip, true);
+
        gpiod_set_value_cansleep(rockchip->rst_gpio, 1);
 
        return 0;
@@ -299,6 +390,7 @@ static void rockchip_pcie_stop_link(struct dw_pcie *pci)
        struct rockchip_pcie *rockchip = to_rockchip_pcie(pci);
 
        rockchip_pcie_disable_ltssm(rockchip);
+       rockchip_pcie_ltssm_trace(rockchip, false);
 }
 
 static int rockchip_pcie_host_init(struct dw_pcie_rp *pp)
-- 
2.7.4


Reply via email to