Skip to content

Commit 27491ac

Browse files
shradhatkwilczynski
authored andcommitted
PCI: dwc: Add debugfs based Statistical Counter support for DWC
Add support to provide Statistical Counter interface to userspace. This set of debug registers are part of the RAS DES feature present in DesignWare PCIe controllers. Co-developed-by: Manivannan Sadhasivam <[email protected]> Signed-off-by: Shradha Todi <[email protected]> Reviewed-by: Manivannan Sadhasivam <[email protected]> Tested-by: Hrishikesh Deleep <[email protected]> Link: https://lore.kernel.org/r/[email protected] [kwilczynski: commit log, tidy up code comments, update documentation, squashed patch that checks if the event counter is supported from https://lore.kernel.org/linux-pci/[email protected]] Signed-off-by: Krzysztof Wilczyński <[email protected]>
1 parent d20ee8e commit 27491ac

File tree

2 files changed

+312
-0
lines changed

2 files changed

+312
-0
lines changed

Documentation/ABI/testing/debugfs-dwc-pcie

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,67 @@ Description: The "rasdes_err_inj" is a directory which can be used to inject
8585

8686
<count>
8787
Number of errors to be injected
88+
89+
What: /sys/kernel/debug/dwc_pcie_<dev>/rasdes_event_counters/<event>/counter_enable
90+
Date: February 2025
91+
Contact: Shradha Todi <[email protected]>
92+
Description: The "rasdes_event_counters" is the directory which can be used
93+
to collect statistical data about the number of times a certain
94+
event has occurred in the controller. The list of possible
95+
events are:
96+
97+
1) EBUF Overflow
98+
2) EBUF Underrun
99+
3) Decode Error
100+
4) Running Disparity Error
101+
5) SKP OS Parity Error
102+
6) SYNC Header Error
103+
7) Rx Valid De-assertion
104+
8) CTL SKP OS Parity Error
105+
9) 1st Retimer Parity Error
106+
10) 2nd Retimer Parity Error
107+
11) Margin CRC and Parity Error
108+
12) Detect EI Infer
109+
13) Receiver Error
110+
14) RX Recovery Req
111+
15) N_FTS Timeout
112+
16) Framing Error
113+
17) Deskew Error
114+
18) Framing Error In L0
115+
19) Deskew Uncompleted Error
116+
20) Bad TLP
117+
21) LCRC Error
118+
22) Bad DLLP
119+
23) Replay Number Rollover
120+
24) Replay Timeout
121+
25) Rx Nak DLLP
122+
26) Tx Nak DLLP
123+
27) Retry TLP
124+
28) FC Timeout
125+
29) Poisoned TLP
126+
30) ECRC Error
127+
31) Unsupported Request
128+
32) Completer Abort
129+
33) Completion Timeout
130+
34) EBUF SKP Add
131+
35) EBUF SKP Del
132+
133+
(RW) Write 1 to enable the event counter and write 0 to disable
134+
the event counter. Read will return whether the counter is
135+
currently enabled or disabled. Counter is disabled by default.
136+
137+
What: /sys/kernel/debug/dwc_pcie_<dev>/rasdes_event_counters/<event>/counter_value
138+
Date: February 2025
139+
Contact: Shradha Todi <[email protected]>
140+
Description: (RO) Read will return the current value of the event counter.
141+
To reset the counter, counter should be disabled first and then
142+
enabled back using the "counter_enable" attribute.
143+
144+
What: /sys/kernel/debug/dwc_pcie_<dev>/rasdes_event_counters/<event>/lane_select
145+
Date: February 2025
146+
Contact: Shradha Todi <[email protected]>
147+
Description: (RW) Some lanes in the event list are lane specific events.
148+
These include events from 1 to 11, as well as, 34 and 35. Write
149+
the lane number for which you wish the counter to be enabled,
150+
disabled, or value dumped. Read will return the current
151+
selected lane number. Lane0 is selected by default.

drivers/pci/controller/dwc/pcie-designware-debugfs.c

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,17 @@
3131

3232
#define ERR_INJ_ENABLE_REG 0x30
3333

34+
#define RAS_DES_EVENT_COUNTER_DATA_REG 0xc
35+
36+
#define RAS_DES_EVENT_COUNTER_CTRL_REG 0x8
37+
#define EVENT_COUNTER_GROUP_SELECT GENMASK(27, 24)
38+
#define EVENT_COUNTER_EVENT_SELECT GENMASK(23, 16)
39+
#define EVENT_COUNTER_LANE_SELECT GENMASK(11, 8)
40+
#define EVENT_COUNTER_STATUS BIT(7)
41+
#define EVENT_COUNTER_ENABLE GENMASK(4, 2)
42+
#define PER_EVENT_ON 0x3
43+
#define PER_EVENT_OFF 0x1
44+
3445
#define DWC_DEBUGFS_BUF_MAX 128
3546

3647
/**
@@ -115,6 +126,63 @@ static const u32 err_inj_type_mask[] = {
115126
EINJ5_TYPE,
116127
};
117128

129+
/**
130+
* struct dwc_pcie_event_counter - Store details about each event counter
131+
* supported in DWC RAS DES
132+
* @name: Name of the error counter
133+
* @group_no: Group number that the event belongs to. The value can range
134+
* from 0 to 4
135+
* @event_no: Event number of the particular event. The value ranges are:
136+
* Group 0: 0 - 10
137+
* Group 1: 5 - 13
138+
* Group 2: 0 - 7
139+
* Group 3: 0 - 5
140+
* Group 4: 0 - 1
141+
*/
142+
struct dwc_pcie_event_counter {
143+
const char *name;
144+
u32 group_no;
145+
u32 event_no;
146+
};
147+
148+
static const struct dwc_pcie_event_counter event_list[] = {
149+
{"ebuf_overflow", 0x0, 0x0},
150+
{"ebuf_underrun", 0x0, 0x1},
151+
{"decode_err", 0x0, 0x2},
152+
{"running_disparity_err", 0x0, 0x3},
153+
{"skp_os_parity_err", 0x0, 0x4},
154+
{"sync_header_err", 0x0, 0x5},
155+
{"rx_valid_deassertion", 0x0, 0x6},
156+
{"ctl_skp_os_parity_err", 0x0, 0x7},
157+
{"retimer_parity_err_1st", 0x0, 0x8},
158+
{"retimer_parity_err_2nd", 0x0, 0x9},
159+
{"margin_crc_parity_err", 0x0, 0xA},
160+
{"detect_ei_infer", 0x1, 0x5},
161+
{"receiver_err", 0x1, 0x6},
162+
{"rx_recovery_req", 0x1, 0x7},
163+
{"n_fts_timeout", 0x1, 0x8},
164+
{"framing_err", 0x1, 0x9},
165+
{"deskew_err", 0x1, 0xa},
166+
{"framing_err_in_l0", 0x1, 0xc},
167+
{"deskew_uncompleted_err", 0x1, 0xd},
168+
{"bad_tlp", 0x2, 0x0},
169+
{"lcrc_err", 0x2, 0x1},
170+
{"bad_dllp", 0x2, 0x2},
171+
{"replay_num_rollover", 0x2, 0x3},
172+
{"replay_timeout", 0x2, 0x4},
173+
{"rx_nak_dllp", 0x2, 0x5},
174+
{"tx_nak_dllp", 0x2, 0x6},
175+
{"retry_tlp", 0x2, 0x7},
176+
{"fc_timeout", 0x3, 0x0},
177+
{"poisoned_tlp", 0x3, 0x1},
178+
{"ecrc_error", 0x3, 0x2},
179+
{"unsupported_request", 0x3, 0x3},
180+
{"completer_abort", 0x3, 0x4},
181+
{"completion_timeout", 0x3, 0x5},
182+
{"ebuf_skp_add", 0x4, 0x0},
183+
{"ebuf_skp_del", 0x4, 0x1},
184+
};
185+
118186
static ssize_t lane_detect_read(struct file *file, char __user *buf,
119187
size_t count, loff_t *ppos)
120188
{
@@ -236,6 +304,145 @@ static ssize_t err_inj_write(struct file *file, const char __user *buf,
236304
return count;
237305
}
238306

307+
static void set_event_number(struct dwc_pcie_rasdes_priv *pdata,
308+
struct dw_pcie *pci, struct dwc_pcie_rasdes_info *rinfo)
309+
{
310+
u32 val;
311+
312+
val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG);
313+
val &= ~EVENT_COUNTER_ENABLE;
314+
val &= ~(EVENT_COUNTER_GROUP_SELECT | EVENT_COUNTER_EVENT_SELECT);
315+
val |= FIELD_PREP(EVENT_COUNTER_GROUP_SELECT, event_list[pdata->idx].group_no);
316+
val |= FIELD_PREP(EVENT_COUNTER_EVENT_SELECT, event_list[pdata->idx].event_no);
317+
dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG, val);
318+
}
319+
320+
static ssize_t counter_enable_read(struct file *file, char __user *buf,
321+
size_t count, loff_t *ppos)
322+
{
323+
struct dwc_pcie_rasdes_priv *pdata = file->private_data;
324+
struct dw_pcie *pci = pdata->pci;
325+
struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info;
326+
char debugfs_buf[DWC_DEBUGFS_BUF_MAX];
327+
ssize_t pos;
328+
u32 val;
329+
330+
mutex_lock(&rinfo->reg_event_lock);
331+
set_event_number(pdata, pci, rinfo);
332+
val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG);
333+
mutex_unlock(&rinfo->reg_event_lock);
334+
val = FIELD_GET(EVENT_COUNTER_STATUS, val);
335+
if (val)
336+
pos = scnprintf(debugfs_buf, DWC_DEBUGFS_BUF_MAX, "Counter Enabled\n");
337+
else
338+
pos = scnprintf(debugfs_buf, DWC_DEBUGFS_BUF_MAX, "Counter Disabled\n");
339+
340+
return simple_read_from_buffer(buf, count, ppos, debugfs_buf, pos);
341+
}
342+
343+
static ssize_t counter_enable_write(struct file *file, const char __user *buf,
344+
size_t count, loff_t *ppos)
345+
{
346+
struct dwc_pcie_rasdes_priv *pdata = file->private_data;
347+
struct dw_pcie *pci = pdata->pci;
348+
struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info;
349+
u32 val, enable;
350+
351+
val = kstrtou32_from_user(buf, count, 0, &enable);
352+
if (val)
353+
return val;
354+
355+
mutex_lock(&rinfo->reg_event_lock);
356+
set_event_number(pdata, pci, rinfo);
357+
val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG);
358+
if (enable)
359+
val |= FIELD_PREP(EVENT_COUNTER_ENABLE, PER_EVENT_ON);
360+
else
361+
val |= FIELD_PREP(EVENT_COUNTER_ENABLE, PER_EVENT_OFF);
362+
363+
dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG, val);
364+
365+
/*
366+
* While enabling the counter, always read the status back to check if
367+
* it is enabled or not. Return error if it is not enabled to let the
368+
* users know that the counter is not supported on the platform.
369+
*/
370+
if (enable) {
371+
val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset +
372+
RAS_DES_EVENT_COUNTER_CTRL_REG);
373+
if (!FIELD_GET(EVENT_COUNTER_STATUS, val)) {
374+
mutex_unlock(&rinfo->reg_event_lock);
375+
return -EOPNOTSUPP;
376+
}
377+
}
378+
379+
mutex_unlock(&rinfo->reg_event_lock);
380+
381+
return count;
382+
}
383+
384+
static ssize_t counter_lane_read(struct file *file, char __user *buf,
385+
size_t count, loff_t *ppos)
386+
{
387+
struct dwc_pcie_rasdes_priv *pdata = file->private_data;
388+
struct dw_pcie *pci = pdata->pci;
389+
struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info;
390+
char debugfs_buf[DWC_DEBUGFS_BUF_MAX];
391+
ssize_t pos;
392+
u32 val;
393+
394+
mutex_lock(&rinfo->reg_event_lock);
395+
set_event_number(pdata, pci, rinfo);
396+
val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG);
397+
mutex_unlock(&rinfo->reg_event_lock);
398+
val = FIELD_GET(EVENT_COUNTER_LANE_SELECT, val);
399+
pos = scnprintf(debugfs_buf, DWC_DEBUGFS_BUF_MAX, "Lane: %d\n", val);
400+
401+
return simple_read_from_buffer(buf, count, ppos, debugfs_buf, pos);
402+
}
403+
404+
static ssize_t counter_lane_write(struct file *file, const char __user *buf,
405+
size_t count, loff_t *ppos)
406+
{
407+
struct dwc_pcie_rasdes_priv *pdata = file->private_data;
408+
struct dw_pcie *pci = pdata->pci;
409+
struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info;
410+
u32 val, lane;
411+
412+
val = kstrtou32_from_user(buf, count, 0, &lane);
413+
if (val)
414+
return val;
415+
416+
mutex_lock(&rinfo->reg_event_lock);
417+
set_event_number(pdata, pci, rinfo);
418+
val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG);
419+
val &= ~(EVENT_COUNTER_LANE_SELECT);
420+
val |= FIELD_PREP(EVENT_COUNTER_LANE_SELECT, lane);
421+
dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG, val);
422+
mutex_unlock(&rinfo->reg_event_lock);
423+
424+
return count;
425+
}
426+
427+
static ssize_t counter_value_read(struct file *file, char __user *buf,
428+
size_t count, loff_t *ppos)
429+
{
430+
struct dwc_pcie_rasdes_priv *pdata = file->private_data;
431+
struct dw_pcie *pci = pdata->pci;
432+
struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info;
433+
char debugfs_buf[DWC_DEBUGFS_BUF_MAX];
434+
ssize_t pos;
435+
u32 val;
436+
437+
mutex_lock(&rinfo->reg_event_lock);
438+
set_event_number(pdata, pci, rinfo);
439+
val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_DATA_REG);
440+
mutex_unlock(&rinfo->reg_event_lock);
441+
pos = scnprintf(debugfs_buf, DWC_DEBUGFS_BUF_MAX, "Counter value: %d\n", val);
442+
443+
return simple_read_from_buffer(buf, count, ppos, debugfs_buf, pos);
444+
}
445+
239446
#define dwc_debugfs_create(name) \
240447
debugfs_create_file(#name, 0644, rasdes_debug, pci, \
241448
&dbg_ ## name ## _fops)
@@ -255,6 +462,23 @@ static const struct file_operations dwc_pcie_err_inj_ops = {
255462
.write = err_inj_write,
256463
};
257464

465+
static const struct file_operations dwc_pcie_counter_enable_ops = {
466+
.open = simple_open,
467+
.read = counter_enable_read,
468+
.write = counter_enable_write,
469+
};
470+
471+
static const struct file_operations dwc_pcie_counter_lane_ops = {
472+
.open = simple_open,
473+
.read = counter_lane_read,
474+
.write = counter_lane_write,
475+
};
476+
477+
static const struct file_operations dwc_pcie_counter_value_ops = {
478+
.open = simple_open,
479+
.read = counter_value_read,
480+
};
481+
258482
static void dwc_pcie_rasdes_debugfs_deinit(struct dw_pcie *pci)
259483
{
260484
struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info;
@@ -265,6 +489,7 @@ static void dwc_pcie_rasdes_debugfs_deinit(struct dw_pcie *pci)
265489
static int dwc_pcie_rasdes_debugfs_init(struct dw_pcie *pci, struct dentry *dir)
266490
{
267491
struct dentry *rasdes_debug, *rasdes_err_inj;
492+
struct dentry *rasdes_event_counter, *rasdes_events;
268493
struct dwc_pcie_rasdes_info *rasdes_info;
269494
struct dwc_pcie_rasdes_priv *priv_tmp;
270495
struct device *dev = pci->dev;
@@ -288,6 +513,7 @@ static int dwc_pcie_rasdes_debugfs_init(struct dw_pcie *pci, struct dentry *dir)
288513
/* Create subdirectories for Debug, Error Injection, Statistics. */
289514
rasdes_debug = debugfs_create_dir("rasdes_debug", dir);
290515
rasdes_err_inj = debugfs_create_dir("rasdes_err_inj", dir);
516+
rasdes_event_counter = debugfs_create_dir("rasdes_event_counter", dir);
291517

292518
mutex_init(&rasdes_info->reg_event_lock);
293519
rasdes_info->ras_cap_offset = ras_cap;
@@ -310,6 +536,28 @@ static int dwc_pcie_rasdes_debugfs_init(struct dw_pcie *pci, struct dentry *dir)
310536
debugfs_create_file(err_inj_list[i].name, 0200, rasdes_err_inj, priv_tmp,
311537
&dwc_pcie_err_inj_ops);
312538
}
539+
540+
/* Create debugfs files for Statistical Counter subdirectory. */
541+
for (i = 0; i < ARRAY_SIZE(event_list); i++) {
542+
priv_tmp = devm_kzalloc(dev, sizeof(*priv_tmp), GFP_KERNEL);
543+
if (!priv_tmp) {
544+
ret = -ENOMEM;
545+
goto err_deinit;
546+
}
547+
548+
priv_tmp->idx = i;
549+
priv_tmp->pci = pci;
550+
rasdes_events = debugfs_create_dir(event_list[i].name, rasdes_event_counter);
551+
if (event_list[i].group_no == 0 || event_list[i].group_no == 4) {
552+
debugfs_create_file("lane_select", 0644, rasdes_events,
553+
priv_tmp, &dwc_pcie_counter_lane_ops);
554+
}
555+
debugfs_create_file("counter_value", 0444, rasdes_events, priv_tmp,
556+
&dwc_pcie_counter_value_ops);
557+
debugfs_create_file("counter_enable", 0644, rasdes_events, priv_tmp,
558+
&dwc_pcie_counter_enable_ops);
559+
}
560+
313561
return 0;
314562

315563
err_deinit:

0 commit comments

Comments
 (0)