On Fri, Jan 09, 2026 at 02:22:10PM +0800, Shawn Lin wrote:
> 在 2026/01/09 星期五 13:55, Manivannan Sadhasivam 写道:
> > On Fri, Jan 09, 2026 at 11:29:49AM +0800, Shawn Lin wrote:
> > > 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;
> >
> > Reverse Xmas order please.
> >
> > > +
> > > + for (loop = 0; loop < PCIE_DBG_LTSSM_HISTORY_CNT; loop++) {
> >
> > s/loop/i?
> >
> > > + 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;
> >
> > Can you use FIELD_ macros here?
> >
> > > +
> > > + /* Two consecutive identical LTSSM means invalid subsequent
> > > data */
> >
> > Interesting. Does the hardware maintain a counter to track the reads? So
> > once
> > you break out of the loop and read it after 5s, you'll start from where you
> > left
> > i.e., the duplicate entry or from the start of the counter again?
> >
>
> Yes, the ring FIFO maintains counters for recording both of last-read-point
> for user to continue to read, and last-valid-point for HW to
> continue to update transition state. So we could start from where we
> left.
>
Ok. Thanks for clarification. It'd be worth to add it to the existing comment.
> > > + 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;
> >
> > I believe L1.0 is not supported.
> >
>
> I'm not sure I follow this comment. state is DW_PCIE_LTSSM_L1_IDLE
> (L1.0) if l1ss is neither 1 nor 2.
>
Ah ok. It was not clear that L1_IDLE is L1.0
- Mani
--
மணிவண்ணன் சதாசிவம்