diff mbox series

PCI: tegra: Add debugfs support to dump LTSSM trace

Message ID 20190618100844.21683-1-vidyas@nvidia.com
State New
Delegated to: Lorenzo Pieralisi
Headers show
Series PCI: tegra: Add debugfs support to dump LTSSM trace | expand

Commit Message

Vidya Sagar June 18, 2019, 10:08 a.m. UTC
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@nvidia.com>
---
 drivers/pci/controller/pci-tegra.c | 201 +++++++++++++++++++++++++++++
 1 file changed, 201 insertions(+)
diff mbox series

Patch

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,
+				&ltssm_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: