Skip to content

Commit 1328334

Browse files
Mani-Sadhasivamkwilczynski
authored andcommitted
PCI: Add debugfs support for exposing PTM context
Precision Time Management (PTM) mechanism defined in PCIe spec r6.0, sec 6.21 allows precise coordination of timing information across multiple components in a PCIe hierarchy with independent local time clocks. PCI core already supports enabling PTM in the root port and endpoint devices through PTM Extended Capability registers. But the PTM context supported by the PTM capable components such as Root Complex (RC) and Endpoint (EP) controllers were not exposed as of now. Part of the reason is that the spec doesn't define how the context information is exposed to the software and left it to the vendor implementation. So there is no standardized way to get access to the context information and each vendor have defined their own way. This commit adds debugfs support to expose the PTM context to userspace from both PCIe RC and EP controllers. Since the context information is exposed in a vendor specific way, the debugfs interface allows the controller drivers to implement callbacks for each attribute, to be called by the generic PTM driver. The Controller drivers are expected to call pcie_ptm_create_debugfs() to create the debugfs attributes for the PTM context and call pcie_ptm_destroy_debugfs() to destroy them. The drivers should also populate the relevant callbacks in the 'struct pcie_ptm_ops' structure based on the controller implementation. Below PTM context are exposed through debugfs: PCIe RC ======= 1. PTM Local clock 2. PTM T2 timestamp 3. PTM T3 timestamp 4. PTM Context valid PCIe EP ======= 1. PTM Local clock 2. PTM T1 timestamp 3. PTM T4 timestamp 4. PTM Master clock 5. PTM Context update Signed-off-by: Manivannan Sadhasivam <[email protected]> [kwilczynski: fix overflow issue reported by Dan Carpenter from https://lore.kernel.org/linux-pci/[email protected]] Signed-off-by: Krzysztof Wilczyński <[email protected]> Link: https://patch.msgid.link/[email protected]
1 parent 0af2f6b commit 1328334

File tree

4 files changed

+416
-0
lines changed

4 files changed

+416
-0
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
What: /sys/kernel/debug/pcie_ptm_*/local_clock
2+
Date: May 2025
3+
Contact: Manivannan Sadhasivam <[email protected]>
4+
Description:
5+
(RO) PTM local clock in nanoseconds. Applicable for both Root
6+
Complex and Endpoint controllers.
7+
8+
What: /sys/kernel/debug/pcie_ptm_*/master_clock
9+
Date: May 2025
10+
Contact: Manivannan Sadhasivam <[email protected]>
11+
Description:
12+
(RO) PTM master clock in nanoseconds. Applicable only for
13+
Endpoint controllers.
14+
15+
What: /sys/kernel/debug/pcie_ptm_*/t1
16+
Date: May 2025
17+
Contact: Manivannan Sadhasivam <[email protected]>
18+
Description:
19+
(RO) PTM T1 timestamp in nanoseconds. Applicable only for
20+
Endpoint controllers.
21+
22+
What: /sys/kernel/debug/pcie_ptm_*/t2
23+
Date: May 2025
24+
Contact: Manivannan Sadhasivam <[email protected]>
25+
Description:
26+
(RO) PTM T2 timestamp in nanoseconds. Applicable only for
27+
Root Complex controllers.
28+
29+
What: /sys/kernel/debug/pcie_ptm_*/t3
30+
Date: May 2025
31+
Contact: Manivannan Sadhasivam <[email protected]>
32+
Description:
33+
(RO) PTM T3 timestamp in nanoseconds. Applicable only for
34+
Root Complex controllers.
35+
36+
What: /sys/kernel/debug/pcie_ptm_*/t4
37+
Date: May 2025
38+
Contact: Manivannan Sadhasivam <[email protected]>
39+
Description:
40+
(RO) PTM T4 timestamp in nanoseconds. Applicable only for
41+
Endpoint controllers.
42+
43+
What: /sys/kernel/debug/pcie_ptm_*/context_update
44+
Date: May 2025
45+
Contact: Manivannan Sadhasivam <[email protected]>
46+
Description:
47+
(RW) Control the PTM context update mode. Applicable only for
48+
Endpoint controllers.
49+
50+
Following values are supported:
51+
52+
* auto = PTM context auto update trigger for every 10ms
53+
54+
* manual = PTM context manual update. Writing 'manual' to this
55+
file triggers PTM context update (default)
56+
57+
What: /sys/kernel/debug/pcie_ptm_*/context_valid
58+
Date: May 2025
59+
Contact: Manivannan Sadhasivam <[email protected]>
60+
Description:
61+
(RW) Control the PTM context validity (local clock timing).
62+
Applicable only for Root Complex controllers. PTM context is
63+
invalidated by hardware if the Root Complex enters low power
64+
mode or changes link frequency.
65+
66+
Following values are supported:
67+
68+
* 0 = PTM context invalid (default)
69+
70+
* 1 = PTM context valid

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18641,6 +18641,7 @@ Q: https://patchwork.kernel.org/project/linux-pci/list/
1864118641
B: https://bugzilla.kernel.org
1864218642
C: irc://irc.oftc.net/linux-pci
1864318643
T: git git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git
18644+
F: Documentation/ABI/testing/debugfs-pcie-ptm
1864418645
F: Documentation/devicetree/bindings/pci/
1864518646
F: drivers/pci/controller/
1864618647
F: drivers/pci/pci-bridge-emul.c

drivers/pci/pcie/ptm.c

Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66

77
#include <linux/bitfield.h>
8+
#include <linux/debugfs.h>
89
#include <linux/module.h>
910
#include <linux/init.h>
1011
#include <linux/pci.h>
@@ -252,3 +253,302 @@ bool pcie_ptm_enabled(struct pci_dev *dev)
252253
return dev->ptm_enabled;
253254
}
254255
EXPORT_SYMBOL(pcie_ptm_enabled);
256+
257+
static ssize_t context_update_write(struct file *file, const char __user *ubuf,
258+
size_t count, loff_t *ppos)
259+
{
260+
struct pci_ptm_debugfs *ptm_debugfs = file->private_data;
261+
char buf[7];
262+
int ret;
263+
u8 mode;
264+
265+
if (!ptm_debugfs->ops->context_update_write)
266+
return -EOPNOTSUPP;
267+
268+
if (count < 1 || count >= sizeof(buf))
269+
return -EINVAL;
270+
271+
ret = copy_from_user(buf, ubuf, count);
272+
if (ret)
273+
return -EFAULT;
274+
275+
buf[count] = '\0';
276+
277+
if (sysfs_streq(buf, "auto"))
278+
mode = PCIE_PTM_CONTEXT_UPDATE_AUTO;
279+
else if (sysfs_streq(buf, "manual"))
280+
mode = PCIE_PTM_CONTEXT_UPDATE_MANUAL;
281+
else
282+
return -EINVAL;
283+
284+
mutex_lock(&ptm_debugfs->lock);
285+
ret = ptm_debugfs->ops->context_update_write(ptm_debugfs->pdata, mode);
286+
mutex_unlock(&ptm_debugfs->lock);
287+
if (ret)
288+
return ret;
289+
290+
return count;
291+
}
292+
293+
static ssize_t context_update_read(struct file *file, char __user *ubuf,
294+
size_t count, loff_t *ppos)
295+
{
296+
struct pci_ptm_debugfs *ptm_debugfs = file->private_data;
297+
char buf[8]; /* Extra space for NULL termination at the end */
298+
ssize_t pos;
299+
u8 mode;
300+
301+
if (!ptm_debugfs->ops->context_update_read)
302+
return -EOPNOTSUPP;
303+
304+
mutex_lock(&ptm_debugfs->lock);
305+
ptm_debugfs->ops->context_update_read(ptm_debugfs->pdata, &mode);
306+
mutex_unlock(&ptm_debugfs->lock);
307+
308+
if (mode == PCIE_PTM_CONTEXT_UPDATE_AUTO)
309+
pos = scnprintf(buf, sizeof(buf), "auto\n");
310+
else
311+
pos = scnprintf(buf, sizeof(buf), "manual\n");
312+
313+
return simple_read_from_buffer(ubuf, count, ppos, buf, pos);
314+
}
315+
316+
static const struct file_operations context_update_fops = {
317+
.open = simple_open,
318+
.read = context_update_read,
319+
.write = context_update_write,
320+
};
321+
322+
static int context_valid_get(void *data, u64 *val)
323+
{
324+
struct pci_ptm_debugfs *ptm_debugfs = data;
325+
bool valid;
326+
int ret;
327+
328+
if (!ptm_debugfs->ops->context_valid_read)
329+
return -EOPNOTSUPP;
330+
331+
mutex_lock(&ptm_debugfs->lock);
332+
ret = ptm_debugfs->ops->context_valid_read(ptm_debugfs->pdata, &valid);
333+
mutex_unlock(&ptm_debugfs->lock);
334+
if (ret)
335+
return ret;
336+
337+
*val = valid;
338+
339+
return 0;
340+
}
341+
342+
static int context_valid_set(void *data, u64 val)
343+
{
344+
struct pci_ptm_debugfs *ptm_debugfs = data;
345+
int ret;
346+
347+
if (!ptm_debugfs->ops->context_valid_write)
348+
return -EOPNOTSUPP;
349+
350+
mutex_lock(&ptm_debugfs->lock);
351+
ret = ptm_debugfs->ops->context_valid_write(ptm_debugfs->pdata, !!val);
352+
mutex_unlock(&ptm_debugfs->lock);
353+
354+
return ret;
355+
}
356+
357+
DEFINE_DEBUGFS_ATTRIBUTE(context_valid_fops, context_valid_get,
358+
context_valid_set, "%llu\n");
359+
360+
static int local_clock_get(void *data, u64 *val)
361+
{
362+
struct pci_ptm_debugfs *ptm_debugfs = data;
363+
u64 clock;
364+
int ret;
365+
366+
if (!ptm_debugfs->ops->local_clock_read)
367+
return -EOPNOTSUPP;
368+
369+
ret = ptm_debugfs->ops->local_clock_read(ptm_debugfs->pdata, &clock);
370+
if (ret)
371+
return ret;
372+
373+
*val = clock;
374+
375+
return 0;
376+
}
377+
378+
DEFINE_DEBUGFS_ATTRIBUTE(local_clock_fops, local_clock_get, NULL, "%llu\n");
379+
380+
static int master_clock_get(void *data, u64 *val)
381+
{
382+
struct pci_ptm_debugfs *ptm_debugfs = data;
383+
u64 clock;
384+
int ret;
385+
386+
if (!ptm_debugfs->ops->master_clock_read)
387+
return -EOPNOTSUPP;
388+
389+
ret = ptm_debugfs->ops->master_clock_read(ptm_debugfs->pdata, &clock);
390+
if (ret)
391+
return ret;
392+
393+
*val = clock;
394+
395+
return 0;
396+
}
397+
398+
DEFINE_DEBUGFS_ATTRIBUTE(master_clock_fops, master_clock_get, NULL, "%llu\n");
399+
400+
static int t1_get(void *data, u64 *val)
401+
{
402+
struct pci_ptm_debugfs *ptm_debugfs = data;
403+
u64 clock;
404+
int ret;
405+
406+
if (!ptm_debugfs->ops->t1_read)
407+
return -EOPNOTSUPP;
408+
409+
ret = ptm_debugfs->ops->t1_read(ptm_debugfs->pdata, &clock);
410+
if (ret)
411+
return ret;
412+
413+
*val = clock;
414+
415+
return 0;
416+
}
417+
418+
DEFINE_DEBUGFS_ATTRIBUTE(t1_fops, t1_get, NULL, "%llu\n");
419+
420+
static int t2_get(void *data, u64 *val)
421+
{
422+
struct pci_ptm_debugfs *ptm_debugfs = data;
423+
u64 clock;
424+
int ret;
425+
426+
if (!ptm_debugfs->ops->t2_read)
427+
return -EOPNOTSUPP;
428+
429+
ret = ptm_debugfs->ops->t2_read(ptm_debugfs->pdata, &clock);
430+
if (ret)
431+
return ret;
432+
433+
*val = clock;
434+
435+
return 0;
436+
}
437+
438+
DEFINE_DEBUGFS_ATTRIBUTE(t2_fops, t2_get, NULL, "%llu\n");
439+
440+
static int t3_get(void *data, u64 *val)
441+
{
442+
struct pci_ptm_debugfs *ptm_debugfs = data;
443+
u64 clock;
444+
int ret;
445+
446+
if (!ptm_debugfs->ops->t3_read)
447+
return -EOPNOTSUPP;
448+
449+
ret = ptm_debugfs->ops->t3_read(ptm_debugfs->pdata, &clock);
450+
if (ret)
451+
return ret;
452+
453+
*val = clock;
454+
455+
return 0;
456+
}
457+
458+
DEFINE_DEBUGFS_ATTRIBUTE(t3_fops, t3_get, NULL, "%llu\n");
459+
460+
static int t4_get(void *data, u64 *val)
461+
{
462+
struct pci_ptm_debugfs *ptm_debugfs = data;
463+
u64 clock;
464+
int ret;
465+
466+
if (!ptm_debugfs->ops->t4_read)
467+
return -EOPNOTSUPP;
468+
469+
ret = ptm_debugfs->ops->t4_read(ptm_debugfs->pdata, &clock);
470+
if (ret)
471+
return ret;
472+
473+
*val = clock;
474+
475+
return 0;
476+
}
477+
478+
DEFINE_DEBUGFS_ATTRIBUTE(t4_fops, t4_get, NULL, "%llu\n");
479+
480+
#define pcie_ptm_create_debugfs_file(pdata, mode, attr) \
481+
do { \
482+
if (ops->attr##_visible && ops->attr##_visible(pdata)) \
483+
debugfs_create_file(#attr, mode, ptm_debugfs->debugfs, \
484+
ptm_debugfs, &attr##_fops); \
485+
} while (0)
486+
487+
/*
488+
* pcie_ptm_create_debugfs() - Create debugfs entries for the PTM context
489+
* @dev: PTM capable component device
490+
* @pdata: Private data of the PTM capable component device
491+
* @ops: PTM callback structure
492+
*
493+
* Create debugfs entries for exposing the PTM context of the PTM capable
494+
* components such as Root Complex and Endpoint controllers.
495+
*
496+
* Return: Pointer to 'struct pci_ptm_debugfs' if success, NULL otherwise.
497+
*/
498+
struct pci_ptm_debugfs *pcie_ptm_create_debugfs(struct device *dev, void *pdata,
499+
const struct pcie_ptm_ops *ops)
500+
{
501+
struct pci_ptm_debugfs *ptm_debugfs;
502+
char *dirname;
503+
int ret;
504+
505+
/* Caller must provide check_capability() callback */
506+
if (!ops->check_capability)
507+
return NULL;
508+
509+
/* Check for PTM capability before creating debugfs attrbutes */
510+
ret = ops->check_capability(pdata);
511+
if (!ret) {
512+
dev_dbg(dev, "PTM capability not present\n");
513+
return NULL;
514+
}
515+
516+
ptm_debugfs = kzalloc(sizeof(*ptm_debugfs), GFP_KERNEL);
517+
if (!ptm_debugfs)
518+
return NULL;
519+
520+
dirname = devm_kasprintf(dev, GFP_KERNEL, "pcie_ptm_%s", dev_name(dev));
521+
if (!dirname)
522+
return NULL;
523+
524+
ptm_debugfs->debugfs = debugfs_create_dir(dirname, NULL);
525+
ptm_debugfs->pdata = pdata;
526+
ptm_debugfs->ops = ops;
527+
mutex_init(&ptm_debugfs->lock);
528+
529+
pcie_ptm_create_debugfs_file(pdata, 0644, context_update);
530+
pcie_ptm_create_debugfs_file(pdata, 0644, context_valid);
531+
pcie_ptm_create_debugfs_file(pdata, 0444, local_clock);
532+
pcie_ptm_create_debugfs_file(pdata, 0444, master_clock);
533+
pcie_ptm_create_debugfs_file(pdata, 0444, t1);
534+
pcie_ptm_create_debugfs_file(pdata, 0444, t2);
535+
pcie_ptm_create_debugfs_file(pdata, 0444, t3);
536+
pcie_ptm_create_debugfs_file(pdata, 0444, t4);
537+
538+
return ptm_debugfs;
539+
}
540+
EXPORT_SYMBOL_GPL(pcie_ptm_create_debugfs);
541+
542+
/*
543+
* pcie_ptm_destroy_debugfs() - Destroy debugfs entries for the PTM context
544+
* @ptm_debugfs: Pointer to the PTM debugfs struct
545+
*/
546+
void pcie_ptm_destroy_debugfs(struct pci_ptm_debugfs *ptm_debugfs)
547+
{
548+
if (!ptm_debugfs)
549+
return;
550+
551+
mutex_destroy(&ptm_debugfs->lock);
552+
debugfs_remove_recursive(ptm_debugfs->debugfs);
553+
}
554+
EXPORT_SYMBOL_GPL(pcie_ptm_destroy_debugfs);

0 commit comments

Comments
 (0)