[PATCH v3 3/3] PCI: dw-rockchip: Add pcie_ltssm_state_transition trace support
Shawn Lin
shawn.lin at rock-chips.com
Sun Jan 11 17:20:00 PST 2026
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 <shawn.lin at rock-chips.com>
---
Changes in v3:
- reorder variables(Mani)
- rename loop to i; rename en to enable(Mani)
- use FIELD_GET(Mani)
- add comment about how the FIFO works(Mani)
Changes in v2:
- use tracepoint
drivers/pci/controller/dwc/pcie-dw-rockchip.c | 104 ++++++++++++++++++++++++++
1 file changed, 104 insertions(+)
diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
index 352f513..344e0b9 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,20 @@
#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_FIFO_RATE_MASK GENMASK(22, 20)
+#define PCIE_DBG_FIFO_L1SUB_MASK GENMASK(10, 8)
+#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 +112,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 +223,89 @@ 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 i, l1ss, prev_val = DW_PCIE_LTSSM_UNKNOWN, rate, val;
+
+ for (i = 0; i < PCIE_DBG_LTSSM_HISTORY_CNT; i++) {
+ val = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_DBG_FIFO_STATUS);
+ rate = FIELD_GET(PCIE_DBG_FIFO_RATE_MASK, val);
+ l1ss = FIELD_GET(PCIE_DBG_FIFO_L1SUB_MASK, val);
+ val = FIELD_GET(PCIE_LTSSM_STATUS_MASK, val);
+
+ /*
+ * Hardware Mechanism: The ring FIFO employs two tracking counters:
+ * - 'last-read-point': maintains the user's last read position
+ * - 'last-valid-point': tracks the hardware's last state update
+ *
+ * Software Handling: When two consecutive LTSSM states are identical,
+ * it indicates invalid subsequent data in the FIFO. In this case, we
+ * skip the remaining entries. The dual-counter design ensures that on
+ * the next state transition, reading can resume from the last user
+ * position.
+ */
+ if ((i > 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 enable)
+{
+ if (enable) {
+ 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 enable)
+{
+}
+#endif
+
static void rockchip_pcie_enable_ltssm(struct rockchip_pcie *rockchip)
{
rockchip_pcie_writel_apb(rockchip, PCIE_CLIENT_ENABLE_LTSSM,
@@ -289,6 +389,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 +402,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
More information about the Linux-rockchip
mailing list