Skip to content

Commit d20ee8e

Browse files
shradhatkwilczynski
authored andcommitted
PCI: dwc: Add debugfs based Error Injection support for DWC
Add support to provide Error Injection interface to userspace. This set of debug registers are part of the RAS DES feature present in DesignWare PCIe controllers. Signed-off-by: Shradha Todi <[email protected]> Reviewed-by: Manivannan Sadhasivam <[email protected]> Reviewed-by: Fan Ni <[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, change debugfs property name from "duplicate_dllp" to "duplicate_tlp"] Signed-off-by: Krzysztof Wilczyński <[email protected]>
1 parent 4fbfa17 commit d20ee8e

File tree

2 files changed

+240
-2
lines changed

2 files changed

+240
-2
lines changed

Documentation/ABI/testing/debugfs-dwc-pcie

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,77 @@ Contact: Shradha Todi <[email protected]>
1111
Description: (RW) Write the lane number to be checked as valid or invalid.
1212
Read will return the status of PIPE RXVALID signal of the
1313
selected lane. The default selected lane is Lane0.
14+
15+
What: /sys/kernel/debug/dwc_pcie_<dev>/rasdes_err_inj/<error>
16+
Date: February 2025
17+
Contact: Shradha Todi <[email protected]>
18+
Description: The "rasdes_err_inj" is a directory which can be used to inject
19+
errors into the system. The possible errors that can be injected
20+
are:
21+
22+
1) tx_lcrc - TLP LCRC error injection TX Path
23+
2) b16_crc_dllp - 16b CRC error injection of ACK/NAK DLLP
24+
3) b16_crc_upd_fc - 16b CRC error injection of Update-FC DLLP
25+
4) tx_ecrc - TLP ECRC error injection TX Path
26+
5) fcrc_tlp - TLP's FCRC error injection TX Path
27+
6) parity_tsos - Parity error of TSOS
28+
7) parity_skpos - Parity error on SKPOS
29+
8) rx_lcrc - LCRC error injection RX Path
30+
9) rx_ecrc - ECRC error injection RX Path
31+
10) tlp_err_seq - TLPs SEQ# error
32+
11) ack_nak_dllp_seq - DLLPS ACK/NAK SEQ# error
33+
12) ack_nak_dllp - ACK/NAK DLLPs transmission block
34+
13) upd_fc_dllp - UpdateFC DLLPs transmission block
35+
14) nak_dllp - Always transmission for NAK DLLP
36+
15) inv_sync_hdr_sym - Invert SYNC header
37+
16) com_pad_ts1 - COM/PAD TS1 order set
38+
17) com_pad_ts2 - COM/PAD TS2 order set
39+
18) com_fts - COM/FTS FTS order set
40+
19) com_idl - COM/IDL E-idle order set
41+
20) end_edb - END/EDB symbol
42+
21) stp_sdp - STP/SDP symbol
43+
22) com_skp - COM/SKP SKP order set
44+
23) posted_tlp_hdr - Posted TLP Header credit value control
45+
24) non_post_tlp_hdr - Non-Posted TLP Header credit value control
46+
25) cmpl_tlp_hdr - Completion TLP Header credit value control
47+
26) posted_tlp_data - Posted TLP Data credit value control
48+
27) non_post_tlp_data - Non-Posted TLP Data credit value control
49+
28) cmpl_tlp_data - Completion TLP Data credit value control
50+
29) duplicate_tlp - Generates duplicate TLPs
51+
30) nullified_tlp - Generates Nullified TLPs
52+
53+
(WO) Write to the attribute will prepare controller to inject
54+
the respective error in the next transmission of data.
55+
56+
Parameter required to write will change in the following ways:
57+
58+
- Errors 9 and 10 are sequence errors. The write command:
59+
60+
echo <count> <diff> > /sys/kernel/debug/dwc_pcie_<dev>/rasdes_err_inj/<error>
61+
62+
<count>
63+
Number of errors to be injected
64+
<diff>
65+
The difference to add or subtract from natural
66+
sequence number to generate sequence error.
67+
Allowed range from -4095 to 4095
68+
69+
- Errors 23 to 28 are credit value error insertions. The write
70+
command:
71+
72+
echo <count> <diff> <vc> > /sys/kernel/debug/dwc_pcie_<dev>/rasdes_err_inj/<error>
73+
74+
<count>
75+
Number of errors to be injected
76+
<diff>
77+
The difference to add or subtract from UpdateFC
78+
credit value. Allowed range from -4095 to 4095
79+
<vc>
80+
Target VC number
81+
82+
- All other errors. The write command:
83+
84+
echo <count> > /sys/kernel/debug/dwc_pcie_<dev>/rasdes_err_inj/<error>
85+
86+
<count>
87+
Number of errors to be injected

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

Lines changed: 166 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,20 @@
1717
#define PIPE_DETECT_LANE BIT(17)
1818
#define LANE_SELECT GENMASK(3, 0)
1919

20+
#define ERR_INJ0_OFF 0x34
21+
#define EINJ_VAL_DIFF GENMASK(28, 16)
22+
#define EINJ_VC_NUM GENMASK(14, 12)
23+
#define EINJ_TYPE_SHIFT 8
24+
#define EINJ0_TYPE GENMASK(11, 8)
25+
#define EINJ1_TYPE BIT(8)
26+
#define EINJ2_TYPE GENMASK(9, 8)
27+
#define EINJ3_TYPE GENMASK(10, 8)
28+
#define EINJ4_TYPE GENMASK(10, 8)
29+
#define EINJ5_TYPE BIT(8)
30+
#define EINJ_COUNT GENMASK(7, 0)
31+
32+
#define ERR_INJ_ENABLE_REG 0x30
33+
2034
#define DWC_DEBUGFS_BUF_MAX 128
2135

2236
/**
@@ -33,6 +47,74 @@ struct dwc_pcie_rasdes_info {
3347
struct mutex reg_event_lock;
3448
};
3549

50+
/**
51+
* struct dwc_pcie_rasdes_priv - Stores file specific private data information
52+
* @pci: Reference to the dw_pcie structure
53+
* @idx: Index of specific file related information in array of structs
54+
*
55+
* All debugfs files will have this struct as its private data.
56+
*/
57+
struct dwc_pcie_rasdes_priv {
58+
struct dw_pcie *pci;
59+
int idx;
60+
};
61+
62+
/**
63+
* struct dwc_pcie_err_inj - Store details about each error injection
64+
* supported by DWC RAS DES
65+
* @name: Name of the error that can be injected
66+
* @err_inj_group: Group number to which the error belongs. The value
67+
* can range from 0 to 5
68+
* @err_inj_type: Each group can have multiple types of error
69+
*/
70+
struct dwc_pcie_err_inj {
71+
const char *name;
72+
u32 err_inj_group;
73+
u32 err_inj_type;
74+
};
75+
76+
static const struct dwc_pcie_err_inj err_inj_list[] = {
77+
{"tx_lcrc", 0x0, 0x0},
78+
{"b16_crc_dllp", 0x0, 0x1},
79+
{"b16_crc_upd_fc", 0x0, 0x2},
80+
{"tx_ecrc", 0x0, 0x3},
81+
{"fcrc_tlp", 0x0, 0x4},
82+
{"parity_tsos", 0x0, 0x5},
83+
{"parity_skpos", 0x0, 0x6},
84+
{"rx_lcrc", 0x0, 0x8},
85+
{"rx_ecrc", 0x0, 0xb},
86+
{"tlp_err_seq", 0x1, 0x0},
87+
{"ack_nak_dllp_seq", 0x1, 0x1},
88+
{"ack_nak_dllp", 0x2, 0x0},
89+
{"upd_fc_dllp", 0x2, 0x1},
90+
{"nak_dllp", 0x2, 0x2},
91+
{"inv_sync_hdr_sym", 0x3, 0x0},
92+
{"com_pad_ts1", 0x3, 0x1},
93+
{"com_pad_ts2", 0x3, 0x2},
94+
{"com_fts", 0x3, 0x3},
95+
{"com_idl", 0x3, 0x4},
96+
{"end_edb", 0x3, 0x5},
97+
{"stp_sdp", 0x3, 0x6},
98+
{"com_skp", 0x3, 0x7},
99+
{"posted_tlp_hdr", 0x4, 0x0},
100+
{"non_post_tlp_hdr", 0x4, 0x1},
101+
{"cmpl_tlp_hdr", 0x4, 0x2},
102+
{"posted_tlp_data", 0x4, 0x4},
103+
{"non_post_tlp_data", 0x4, 0x5},
104+
{"cmpl_tlp_data", 0x4, 0x6},
105+
{"duplicate_tlp", 0x5, 0x0},
106+
{"nullified_tlp", 0x5, 0x1},
107+
};
108+
109+
static const u32 err_inj_type_mask[] = {
110+
EINJ0_TYPE,
111+
EINJ1_TYPE,
112+
EINJ2_TYPE,
113+
EINJ3_TYPE,
114+
EINJ4_TYPE,
115+
EINJ5_TYPE,
116+
};
117+
36118
static ssize_t lane_detect_read(struct file *file, char __user *buf,
37119
size_t count, loff_t *ppos)
38120
{
@@ -96,6 +178,64 @@ static ssize_t rx_valid_write(struct file *file, const char __user *buf,
96178
return lane_detect_write(file, buf, count, ppos);
97179
}
98180

181+
static ssize_t err_inj_write(struct file *file, const char __user *buf,
182+
size_t count, loff_t *ppos)
183+
{
184+
struct dwc_pcie_rasdes_priv *pdata = file->private_data;
185+
struct dw_pcie *pci = pdata->pci;
186+
struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info;
187+
u32 val, counter, vc_num, err_group, type_mask;
188+
int val_diff = 0;
189+
char *kern_buf;
190+
191+
err_group = err_inj_list[pdata->idx].err_inj_group;
192+
type_mask = err_inj_type_mask[err_group];
193+
194+
kern_buf = memdup_user_nul(buf, count);
195+
if (IS_ERR(kern_buf))
196+
return PTR_ERR(kern_buf);
197+
198+
if (err_group == 4) {
199+
val = sscanf(kern_buf, "%u %d %u", &counter, &val_diff, &vc_num);
200+
if ((val != 3) || (val_diff < -4095 || val_diff > 4095)) {
201+
kfree(kern_buf);
202+
return -EINVAL;
203+
}
204+
} else if (err_group == 1) {
205+
val = sscanf(kern_buf, "%u %d", &counter, &val_diff);
206+
if ((val != 2) || (val_diff < -4095 || val_diff > 4095)) {
207+
kfree(kern_buf);
208+
return -EINVAL;
209+
}
210+
} else {
211+
val = kstrtou32(kern_buf, 0, &counter);
212+
if (val) {
213+
kfree(kern_buf);
214+
return val;
215+
}
216+
}
217+
218+
val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + ERR_INJ0_OFF + (0x4 * err_group));
219+
val &= ~(type_mask | EINJ_COUNT);
220+
val |= ((err_inj_list[pdata->idx].err_inj_type << EINJ_TYPE_SHIFT) & type_mask);
221+
val |= FIELD_PREP(EINJ_COUNT, counter);
222+
223+
if (err_group == 1 || err_group == 4) {
224+
val &= ~(EINJ_VAL_DIFF);
225+
val |= FIELD_PREP(EINJ_VAL_DIFF, val_diff);
226+
}
227+
if (err_group == 4) {
228+
val &= ~(EINJ_VC_NUM);
229+
val |= FIELD_PREP(EINJ_VC_NUM, vc_num);
230+
}
231+
232+
dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + ERR_INJ0_OFF + (0x4 * err_group), val);
233+
dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + ERR_INJ_ENABLE_REG, (0x1 << err_group));
234+
235+
kfree(kern_buf);
236+
return count;
237+
}
238+
99239
#define dwc_debugfs_create(name) \
100240
debugfs_create_file(#name, 0644, rasdes_debug, pci, \
101241
&dbg_ ## name ## _fops)
@@ -110,6 +250,11 @@ static const struct file_operations dbg_ ## name ## _fops = { \
110250
DWC_DEBUGFS_FOPS(lane_detect);
111251
DWC_DEBUGFS_FOPS(rx_valid);
112252

253+
static const struct file_operations dwc_pcie_err_inj_ops = {
254+
.open = simple_open,
255+
.write = err_inj_write,
256+
};
257+
113258
static void dwc_pcie_rasdes_debugfs_deinit(struct dw_pcie *pci)
114259
{
115260
struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info;
@@ -119,10 +264,11 @@ static void dwc_pcie_rasdes_debugfs_deinit(struct dw_pcie *pci)
119264

120265
static int dwc_pcie_rasdes_debugfs_init(struct dw_pcie *pci, struct dentry *dir)
121266
{
122-
struct dentry *rasdes_debug;
267+
struct dentry *rasdes_debug, *rasdes_err_inj;
123268
struct dwc_pcie_rasdes_info *rasdes_info;
269+
struct dwc_pcie_rasdes_priv *priv_tmp;
124270
struct device *dev = pci->dev;
125-
int ras_cap;
271+
int ras_cap, i, ret;
126272

127273
/*
128274
* If a given SoC has no RAS DES capability, the following call is
@@ -141,6 +287,7 @@ static int dwc_pcie_rasdes_debugfs_init(struct dw_pcie *pci, struct dentry *dir)
141287

142288
/* Create subdirectories for Debug, Error Injection, Statistics. */
143289
rasdes_debug = debugfs_create_dir("rasdes_debug", dir);
290+
rasdes_err_inj = debugfs_create_dir("rasdes_err_inj", dir);
144291

145292
mutex_init(&rasdes_info->reg_event_lock);
146293
rasdes_info->ras_cap_offset = ras_cap;
@@ -150,7 +297,24 @@ static int dwc_pcie_rasdes_debugfs_init(struct dw_pcie *pci, struct dentry *dir)
150297
dwc_debugfs_create(lane_detect);
151298
dwc_debugfs_create(rx_valid);
152299

300+
/* Create debugfs files for Error Injection subdirectory. */
301+
for (i = 0; i < ARRAY_SIZE(err_inj_list); i++) {
302+
priv_tmp = devm_kzalloc(dev, sizeof(*priv_tmp), GFP_KERNEL);
303+
if (!priv_tmp) {
304+
ret = -ENOMEM;
305+
goto err_deinit;
306+
}
307+
308+
priv_tmp->idx = i;
309+
priv_tmp->pci = pci;
310+
debugfs_create_file(err_inj_list[i].name, 0200, rasdes_err_inj, priv_tmp,
311+
&dwc_pcie_err_inj_ops);
312+
}
153313
return 0;
314+
315+
err_deinit:
316+
dwc_pcie_rasdes_debugfs_deinit(pci);
317+
return ret;
154318
}
155319

156320
void dwc_pcie_debugfs_deinit(struct dw_pcie *pci)

0 commit comments

Comments
 (0)