Add support to dump LTSSM trace through a port specific debugfs entry 'ltssm_trace'. This is particular useful to debug link up issues and speed change issues. Trace is dumped in the following format. [root@alarm ~]# cat /sys/kernel/debug/pcie/0/ltssm_trace Trace Reg Val Major Minor-Tx Minor-Rx ---------------------------------------------------- [0x00090824] detect active active [0x001b0864] detect wait wait [0x001208a4] detect retry retry [0x000018e4] polling active active [0x00091924] polling config config [0x00002964] config link start link start [0x000929a4] config link accept link accept [0x001b29e4] config lane wait lane wait [0x00122a24] config lane accept lane accept [0x00362a64] config complete complete [0x00242aa4] config idle idle [0x00004ae4] l0 normal normal [0x00054b24] l0 pwrup normal [0x00387b64] recovery rcvrlock finish pkt [0x00007ba4] recovery rcvrlock rcvrlock [0x00097be4] recovery rcvrcfg rcvrcfg [0x001b7c24] recovery idle idle [0x00004c64] l0 normal normal Signed-off-by: Vidya Sagar <vidyas@xxxxxxxxxx> --- drivers/pci/controller/pci-tegra.c | 201 +++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 464ba2538d52..2b5b43e09ced 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -177,6 +177,26 @@ #define AFI_PEXBIAS_CTRL_0 0x168 +#define RP_LTSSM_DBGREG 0xe44 +#define RP_LTSSM_DBGREG_LINKFSM16 BIT(16) + +#define RP_LTSSM_TRACE_CTRL 0xe50 +#define LTSSM_TRACE_CTRL_CLEAR_STORE_EN BIT(0) +#define LTSSM_TRACE_CTRL_CLEAR_RAM BIT(2) +#define LTSSM_TRACE_CTRL_TRIG_ON_EVENT BIT(3) +#define LTSSM_TRACE_CTRL_TRIG_LTSSM_MAJOR_OFFSET 4 +#define LTSSM_TRACE_CTRL_TRIG_PTX_LTSSM_MINOR_OFFSET 8 +#define LTSSM_TRACE_CTRL_TRIG_PRX_LTSSM_MAJOR_OFFSET 11 + +#define RP_LTSSM_TRACE_STS 0xe54 +#define LTSSM_TRACE_STS_PRX_MINOR(reg) (((reg) >> 19) & 0x7) +#define LTSSM_TRACE_STS_PTX_MINOR(reg) (((reg) >> 16) & 0x7) +#define LTSSM_TRACE_STS_MAJOR(reg) (((reg) >> 12) & 0xf) +#define LTSSM_TRACE_STS_READ_DATA_VALID(reg) (((reg) >> 11) & 0x1) +#define LTSSM_TRACE_STS_READ_ADDR(reg) ((reg) << 6) +#define LTSSM_TRACE_STS_WRITE_POINTER(reg) (((reg) >> 1) & 0x1f) +#define LTSSM_TRACE_STS_RAM_FULL(reg) ((reg) & 0x1) + #define RP_VEND_XP 0x00000f00 #define RP_VEND_XP_DL_UP (1 << 30) @@ -321,6 +341,7 @@ struct tegra_pcie_port { unsigned int lanes; struct phy **phys; + struct dentry *debugfs; }; struct tegra_pcie_bus { @@ -2348,9 +2369,184 @@ static void tegra_pcie_debugfs_exit(struct tegra_pcie *pcie) pcie->debugfs = NULL; } +struct ltssm_major_state { + const char *name; + const char *minor[8]; +}; + +struct ltssm_state { + struct ltssm_major_state major[12]; +}; + +static const struct ltssm_state ltssm_state = { + .major = { + { + .name = "detect", + .minor = {"quiet", + "active", + "retry", + "wait", + "entry"} + }, { + .name = "polling", + .minor = {"active", + "config", + "idle", + NULL, + "compliance", + "cspeed"} + }, { + .name = "config", + .minor = {"link start", + "link accept", + "lane accept", + "lane wait", + "idle", + "pwrup", + "complete"} + }, { + .name = NULL, + .minor = {NULL} + }, { + .name = "l0", + .minor = {"normal", + "l0s entry", + "l0s idle", + "l0s wait", + "l0s fts", + "pwrup"} + }, { + .name = "l1", + .minor = {"entry", + "waitrx", + "idle", + "wait", + "pwrup", + "beacon entry", + "beacon exit"} + }, { + .name = "l2", + .minor = {"entry", + "waitrx", + "transmitwake", + "idle"} + }, { + .name = "recovery", + .minor = {"rcvrlock", + "rcvrcfg", + "speed", + "idle", + NULL, + NULL, + NULL, + "finish pkt"} + }, { + .name = "loopback", + .minor = {"entry", + "active", + "idle", + "exit", + "speed", + "pre speed"} + }, { + .name = "hotreset", + .minor = {NULL} + }, { + .name = "disabled", + .minor = {NULL} + }, { + .name = "txchar", + .minor = {NULL} + } + } +}; + +static const char *ltssm_get_major(unsigned int major) +{ + const char *state; + + state = ltssm_state.major[major].name; + if (!state) + return "unknown"; + + return state; +} + +static const char *ltssm_get_minor(unsigned int major, unsigned int minor) +{ + const char *state; + + state = ltssm_state.major[major].minor[minor]; + if (!state) + return "unknown"; + + return state; +} + +static int ltssm_trace_show(struct seq_file *s, void *what) +{ + struct tegra_pcie_port *port = s->private; + unsigned int ridx, widx, entries; + u32 value; + + value = readl(port->base + RP_LTSSM_TRACE_STS); + widx = LTSSM_TRACE_STS_WRITE_POINTER(value); + entries = LTSSM_TRACE_STS_RAM_FULL(value) ? 32 : widx; + + seq_puts(s, "Trace Reg Val Major Minor-Tx Minor-Rx\n"); + seq_puts(s, "----------------------------------------------------\n"); + for (ridx = 0; ridx < entries; ridx++) { + value = LTSSM_TRACE_STS_READ_ADDR(ridx); + writel(value, port->base + RP_LTSSM_TRACE_STS); + value = readl(port->base + RP_LTSSM_TRACE_STS); + + seq_printf(s, "[0x%08x] %-10s %-13s %s\n", value, + ltssm_get_major(LTSSM_TRACE_STS_MAJOR(value)), + ltssm_get_minor(LTSSM_TRACE_STS_MAJOR(value), + LTSSM_TRACE_STS_PTX_MINOR(value)), + ltssm_get_minor(LTSSM_TRACE_STS_MAJOR(value), + LTSSM_TRACE_STS_PRX_MINOR(value))); + } + + /* Clear Trace RAM */ + value = readl(port->base + RP_LTSSM_TRACE_CTRL); + value |= LTSSM_TRACE_CTRL_CLEAR_RAM; + writel(value, port->base + RP_LTSSM_TRACE_CTRL); + value &= ~LTSSM_TRACE_CTRL_CLEAR_RAM; + writel(value, port->base + RP_LTSSM_TRACE_CTRL); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(ltssm_trace); + +static int tegra_pcie_port_debugfs_init(struct tegra_pcie_port *port) +{ + struct dentry *d; + char port_name[2] = {0}; + + snprintf(port_name, sizeof(port_name), "%d", port->index); + port->debugfs = debugfs_create_dir(port_name, + port->pcie->debugfs); + if (!port->debugfs) + return -ENOMEM; + + d = debugfs_create_file("ltssm_trace", 0444, port->debugfs, port, + <ssm_trace_fops); + if (!d) + goto remove; + + return 0; + +remove: + debugfs_remove_recursive(port->debugfs); + port->debugfs = NULL; + return -ENOMEM; +} + static int tegra_pcie_debugfs_init(struct tegra_pcie *pcie) { struct dentry *file; + struct tegra_pcie_port *port; pcie->debugfs = debugfs_create_dir("pcie", NULL); if (!pcie->debugfs) @@ -2361,6 +2557,11 @@ static int tegra_pcie_debugfs_init(struct tegra_pcie *pcie) if (!file) goto remove; + list_for_each_entry(port, &pcie->ports, list) { + if (tegra_pcie_port_debugfs_init(port)) + goto remove; + } + return 0; remove: -- 2.17.1