Skip to content

Commit 4fbfa17

Browse files
shradhatkwilczynski
authored andcommitted
PCI: dwc: Add debugfs based Silicon Debug support for DWC
Add support to provide Silicon Debug 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]> 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 Kconfig and drop "default y", tidy up code comments, squashed patch that fixes a NULL pointer dereference when debugfs is already unavailable during clean-up from https://lore.kernel.org/linux-pci/[email protected], refactor dwc_pcie_debugfs_init() to not return errors, squashed patch that changes how lack of the RAS DES capability is handled from https://lore.kernel.org/linux-pci/20250304151814.6xu7cbpwpqrvcad5@thinkpad] Signed-off-by: Krzysztof Wilczyński <[email protected]>
1 parent efaf16d commit 4fbfa17

File tree

9 files changed

+245
-0
lines changed

9 files changed

+245
-0
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
What: /sys/kernel/debug/dwc_pcie_<dev>/rasdes_debug/lane_detect
2+
Date: February 2025
3+
Contact: Shradha Todi <[email protected]>
4+
Description: (RW) Write the lane number to be checked for detection. Read
5+
will return whether PHY indicates receiver detection on the
6+
selected lane. The default selected lane is Lane0.
7+
8+
What: /sys/kernel/debug/dwc_pcie_<dev>/rasdes_debug/rx_valid
9+
Date: February 2025
10+
Contact: Shradha Todi <[email protected]>
11+
Description: (RW) Write the lane number to be checked as valid or invalid.
12+
Read will return the status of PIPE RXVALID signal of the
13+
selected lane. The default selected lane is Lane0.

drivers/pci/controller/dwc/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@ menu "DesignWare-based PCIe controllers"
66
config PCIE_DW
77
bool
88

9+
config PCIE_DW_DEBUGFS
10+
bool "DesignWare PCIe debugfs entries"
11+
depends on DEBUG_FS
12+
depends on PCIE_DW_HOST || PCIE_DW_EP
13+
help
14+
Say Y here to enable debugfs entries for the PCIe controller. These
15+
entries provide various debug features related to the controller and
16+
expose the RAS DES capabilities such as Silicon Debug, Error Injection
17+
and Statistical Counters.
18+
919
config PCIE_DW_HOST
1020
bool
1121
select PCIE_DW

drivers/pci/controller/dwc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# SPDX-License-Identifier: GPL-2.0
22
obj-$(CONFIG_PCIE_DW) += pcie-designware.o
3+
obj-$(CONFIG_PCIE_DW_DEBUGFS) += pcie-designware-debugfs.o
34
obj-$(CONFIG_PCIE_DW_HOST) += pcie-designware-host.o
45
obj-$(CONFIG_PCIE_DW_EP) += pcie-designware-ep.o
56
obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Synopsys DesignWare PCIe controller debugfs driver
4+
*
5+
* Copyright (C) 2025 Samsung Electronics Co., Ltd.
6+
* http://www.samsung.com
7+
*
8+
* Author: Shradha Todi <[email protected]>
9+
*/
10+
11+
#include <linux/debugfs.h>
12+
13+
#include "pcie-designware.h"
14+
15+
#define SD_STATUS_L1LANE_REG 0xb0
16+
#define PIPE_RXVALID BIT(18)
17+
#define PIPE_DETECT_LANE BIT(17)
18+
#define LANE_SELECT GENMASK(3, 0)
19+
20+
#define DWC_DEBUGFS_BUF_MAX 128
21+
22+
/**
23+
* struct dwc_pcie_rasdes_info - Stores controller common information
24+
* @ras_cap_offset: RAS DES vendor specific extended capability offset
25+
* @reg_event_lock: Mutex used for RAS DES shadow event registers
26+
*
27+
* Any parameter constant to all files of the debugfs hierarchy for a single
28+
* controller will be stored in this struct. It is allocated and assigned to
29+
* controller specific struct dw_pcie during initialization.
30+
*/
31+
struct dwc_pcie_rasdes_info {
32+
u32 ras_cap_offset;
33+
struct mutex reg_event_lock;
34+
};
35+
36+
static ssize_t lane_detect_read(struct file *file, char __user *buf,
37+
size_t count, loff_t *ppos)
38+
{
39+
struct dw_pcie *pci = file->private_data;
40+
struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info;
41+
char debugfs_buf[DWC_DEBUGFS_BUF_MAX];
42+
ssize_t pos;
43+
u32 val;
44+
45+
val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + SD_STATUS_L1LANE_REG);
46+
val = FIELD_GET(PIPE_DETECT_LANE, val);
47+
if (val)
48+
pos = scnprintf(debugfs_buf, DWC_DEBUGFS_BUF_MAX, "Lane Detected\n");
49+
else
50+
pos = scnprintf(debugfs_buf, DWC_DEBUGFS_BUF_MAX, "Lane Undetected\n");
51+
52+
return simple_read_from_buffer(buf, count, ppos, debugfs_buf, pos);
53+
}
54+
55+
static ssize_t lane_detect_write(struct file *file, const char __user *buf,
56+
size_t count, loff_t *ppos)
57+
{
58+
struct dw_pcie *pci = file->private_data;
59+
struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info;
60+
u32 lane, val;
61+
62+
val = kstrtou32_from_user(buf, count, 0, &lane);
63+
if (val)
64+
return val;
65+
66+
val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + SD_STATUS_L1LANE_REG);
67+
val &= ~(LANE_SELECT);
68+
val |= FIELD_PREP(LANE_SELECT, lane);
69+
dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + SD_STATUS_L1LANE_REG, val);
70+
71+
return count;
72+
}
73+
74+
static ssize_t rx_valid_read(struct file *file, char __user *buf,
75+
size_t count, loff_t *ppos)
76+
{
77+
struct dw_pcie *pci = file->private_data;
78+
struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info;
79+
char debugfs_buf[DWC_DEBUGFS_BUF_MAX];
80+
ssize_t pos;
81+
u32 val;
82+
83+
val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + SD_STATUS_L1LANE_REG);
84+
val = FIELD_GET(PIPE_RXVALID, val);
85+
if (val)
86+
pos = scnprintf(debugfs_buf, DWC_DEBUGFS_BUF_MAX, "RX Valid\n");
87+
else
88+
pos = scnprintf(debugfs_buf, DWC_DEBUGFS_BUF_MAX, "RX Invalid\n");
89+
90+
return simple_read_from_buffer(buf, count, ppos, debugfs_buf, pos);
91+
}
92+
93+
static ssize_t rx_valid_write(struct file *file, const char __user *buf,
94+
size_t count, loff_t *ppos)
95+
{
96+
return lane_detect_write(file, buf, count, ppos);
97+
}
98+
99+
#define dwc_debugfs_create(name) \
100+
debugfs_create_file(#name, 0644, rasdes_debug, pci, \
101+
&dbg_ ## name ## _fops)
102+
103+
#define DWC_DEBUGFS_FOPS(name) \
104+
static const struct file_operations dbg_ ## name ## _fops = { \
105+
.open = simple_open, \
106+
.read = name ## _read, \
107+
.write = name ## _write \
108+
}
109+
110+
DWC_DEBUGFS_FOPS(lane_detect);
111+
DWC_DEBUGFS_FOPS(rx_valid);
112+
113+
static void dwc_pcie_rasdes_debugfs_deinit(struct dw_pcie *pci)
114+
{
115+
struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info;
116+
117+
mutex_destroy(&rinfo->reg_event_lock);
118+
}
119+
120+
static int dwc_pcie_rasdes_debugfs_init(struct dw_pcie *pci, struct dentry *dir)
121+
{
122+
struct dentry *rasdes_debug;
123+
struct dwc_pcie_rasdes_info *rasdes_info;
124+
struct device *dev = pci->dev;
125+
int ras_cap;
126+
127+
/*
128+
* If a given SoC has no RAS DES capability, the following call is
129+
* bound to return an error, breaking some existing platforms. So,
130+
* return 0 here, as this is not necessarily an error.
131+
*/
132+
ras_cap = dw_pcie_find_rasdes_capability(pci);
133+
if (!ras_cap) {
134+
dev_dbg(dev, "no RAS DES capability available\n");
135+
return 0;
136+
}
137+
138+
rasdes_info = devm_kzalloc(dev, sizeof(*rasdes_info), GFP_KERNEL);
139+
if (!rasdes_info)
140+
return -ENOMEM;
141+
142+
/* Create subdirectories for Debug, Error Injection, Statistics. */
143+
rasdes_debug = debugfs_create_dir("rasdes_debug", dir);
144+
145+
mutex_init(&rasdes_info->reg_event_lock);
146+
rasdes_info->ras_cap_offset = ras_cap;
147+
pci->debugfs->rasdes_info = rasdes_info;
148+
149+
/* Create debugfs files for Debug subdirectory. */
150+
dwc_debugfs_create(lane_detect);
151+
dwc_debugfs_create(rx_valid);
152+
153+
return 0;
154+
}
155+
156+
void dwc_pcie_debugfs_deinit(struct dw_pcie *pci)
157+
{
158+
if (!pci->debugfs)
159+
return;
160+
161+
dwc_pcie_rasdes_debugfs_deinit(pci);
162+
debugfs_remove_recursive(pci->debugfs->debug_dir);
163+
}
164+
165+
void dwc_pcie_debugfs_init(struct dw_pcie *pci)
166+
{
167+
char dirname[DWC_DEBUGFS_BUF_MAX];
168+
struct device *dev = pci->dev;
169+
struct debugfs_info *debugfs;
170+
struct dentry *dir;
171+
int err;
172+
173+
/* Create main directory for each platform driver. */
174+
snprintf(dirname, DWC_DEBUGFS_BUF_MAX, "dwc_pcie_%s", dev_name(dev));
175+
dir = debugfs_create_dir(dirname, NULL);
176+
debugfs = devm_kzalloc(dev, sizeof(*debugfs), GFP_KERNEL);
177+
if (!debugfs)
178+
return;
179+
180+
debugfs->debug_dir = dir;
181+
pci->debugfs = debugfs;
182+
err = dwc_pcie_rasdes_debugfs_init(pci, dir);
183+
if (err)
184+
dev_err(dev, "failed to initialize RAS DES debugfs, err=%d\n",
185+
err);
186+
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,7 @@ void dw_pcie_ep_cleanup(struct dw_pcie_ep *ep)
666666
{
667667
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
668668

669+
dwc_pcie_debugfs_deinit(pci);
669670
dw_pcie_edma_remove(pci);
670671
}
671672
EXPORT_SYMBOL_GPL(dw_pcie_ep_cleanup);
@@ -837,6 +838,8 @@ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep)
837838

838839
dw_pcie_ep_init_non_sticky_registers(pci);
839840

841+
dwc_pcie_debugfs_init(pci);
842+
840843
return 0;
841844

842845
err_remove_edma:

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,8 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
548548
if (pp->ops->post_init)
549549
pp->ops->post_init(pp);
550550

551+
dwc_pcie_debugfs_init(pci);
552+
551553
return 0;
552554

553555
err_stop_link:
@@ -572,6 +574,8 @@ void dw_pcie_host_deinit(struct dw_pcie_rp *pp)
572574
{
573575
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
574576

577+
dwc_pcie_debugfs_deinit(pci);
578+
575579
pci_stop_root_bus(pp->bridge->bus);
576580
pci_remove_root_bus(pp->bridge->bus);
577581

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,12 @@ static u16 dw_pcie_find_vsec_capability(struct dw_pcie *pci,
323323
return 0;
324324
}
325325

326+
u16 dw_pcie_find_rasdes_capability(struct dw_pcie *pci)
327+
{
328+
return dw_pcie_find_vsec_capability(pci, dwc_pcie_rasdes_vsec_ids);
329+
}
330+
EXPORT_SYMBOL_GPL(dw_pcie_find_rasdes_capability);
331+
326332
int dw_pcie_read(void __iomem *addr, int size, u32 *val)
327333
{
328334
if (!IS_ALIGNED((uintptr_t)addr, size)) {

drivers/pci/controller/dwc/pcie-designware.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,11 @@ struct dw_pcie_ops {
437437
void (*stop_link)(struct dw_pcie *pcie);
438438
};
439439

440+
struct debugfs_info {
441+
struct dentry *debug_dir;
442+
void *rasdes_info;
443+
};
444+
440445
struct dw_pcie {
441446
struct device *dev;
442447
void __iomem *dbi_base;
@@ -465,6 +470,7 @@ struct dw_pcie {
465470
struct reset_control_bulk_data core_rsts[DW_PCIE_NUM_CORE_RSTS];
466471
struct gpio_desc *pe_rst;
467472
bool suspended;
473+
struct debugfs_info *debugfs;
468474
};
469475

470476
#define to_dw_pcie_from_pp(port) container_of((port), struct dw_pcie, pp)
@@ -478,6 +484,7 @@ void dw_pcie_version_detect(struct dw_pcie *pci);
478484

479485
u8 dw_pcie_find_capability(struct dw_pcie *pci, u8 cap);
480486
u16 dw_pcie_find_ext_capability(struct dw_pcie *pci, u8 cap);
487+
u16 dw_pcie_find_rasdes_capability(struct dw_pcie *pci);
481488

482489
int dw_pcie_read(void __iomem *addr, int size, u32 *val);
483490
int dw_pcie_write(void __iomem *addr, int size, u32 val);
@@ -806,4 +813,17 @@ dw_pcie_ep_get_func_from_ep(struct dw_pcie_ep *ep, u8 func_no)
806813
return NULL;
807814
}
808815
#endif
816+
817+
#ifdef CONFIG_PCIE_DW_DEBUGFS
818+
void dwc_pcie_debugfs_init(struct dw_pcie *pci);
819+
void dwc_pcie_debugfs_deinit(struct dw_pcie *pci);
820+
#else
821+
static inline void dwc_pcie_debugfs_init(struct dw_pcie *pci)
822+
{
823+
}
824+
static inline void dwc_pcie_debugfs_deinit(struct dw_pcie *pci)
825+
{
826+
}
827+
#endif
828+
809829
#endif /* _PCIE_DESIGNWARE_H */

include/linux/pcie-dwc.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ static const struct dwc_pcie_vsec_id dwc_pcie_rasdes_vsec_ids[] = {
2828
.vsec_id = 0x02, .vsec_rev = 0x4 },
2929
{ .vendor_id = PCI_VENDOR_ID_QCOM,
3030
.vsec_id = 0x02, .vsec_rev = 0x4 },
31+
{ .vendor_id = PCI_VENDOR_ID_SAMSUNG,
32+
.vsec_id = 0x02, .vsec_rev = 0x4 },
3133
{}
3234
};
3335

0 commit comments

Comments
 (0)