xref: /kernel/linux/linux-5.10/drivers/pci/pcie/ptm.c (revision 8c2ecf20)
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * PCI Express Precision Time Measurement
4 * Copyright (c) 2016, Intel Corporation.
5 */
6
7#include <linux/module.h>
8#include <linux/init.h>
9#include <linux/pci.h>
10#include "../pci.h"
11
12static void pci_ptm_info(struct pci_dev *dev)
13{
14	char clock_desc[8];
15
16	switch (dev->ptm_granularity) {
17	case 0:
18		snprintf(clock_desc, sizeof(clock_desc), "unknown");
19		break;
20	case 255:
21		snprintf(clock_desc, sizeof(clock_desc), ">254ns");
22		break;
23	default:
24		snprintf(clock_desc, sizeof(clock_desc), "%uns",
25			 dev->ptm_granularity);
26		break;
27	}
28	pci_info(dev, "PTM enabled%s, %s granularity\n",
29		 dev->ptm_root ? " (root)" : "", clock_desc);
30}
31
32void pci_ptm_init(struct pci_dev *dev)
33{
34	int pos;
35	u32 cap, ctrl;
36	u8 local_clock;
37	struct pci_dev *ups;
38
39	if (!pci_is_pcie(dev))
40		return;
41
42	/*
43	 * Enable PTM only on interior devices (root ports, switch ports,
44	 * etc.) on the assumption that it causes no link traffic until an
45	 * endpoint enables it.
46	 */
47	if ((pci_pcie_type(dev) == PCI_EXP_TYPE_ENDPOINT ||
48	     pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END))
49		return;
50
51	/*
52	 * Switch Downstream Ports are not permitted to have a PTM
53	 * capability; their PTM behavior is controlled by the Upstream
54	 * Port (PCIe r5.0, sec 7.9.16).
55	 */
56	ups = pci_upstream_bridge(dev);
57	if (pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM &&
58	    ups && ups->ptm_enabled) {
59		dev->ptm_granularity = ups->ptm_granularity;
60		dev->ptm_enabled = 1;
61		return;
62	}
63
64	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_PTM);
65	if (!pos)
66		return;
67
68	pci_read_config_dword(dev, pos + PCI_PTM_CAP, &cap);
69	local_clock = (cap & PCI_PTM_GRANULARITY_MASK) >> 8;
70
71	/*
72	 * There's no point in enabling PTM unless it's enabled in the
73	 * upstream device or this device can be a PTM Root itself.  Per
74	 * the spec recommendation (PCIe r3.1, sec 7.32.3), select the
75	 * furthest upstream Time Source as the PTM Root.
76	 */
77	if (ups && ups->ptm_enabled) {
78		ctrl = PCI_PTM_CTRL_ENABLE;
79		if (ups->ptm_granularity == 0)
80			dev->ptm_granularity = 0;
81		else if (ups->ptm_granularity > local_clock)
82			dev->ptm_granularity = ups->ptm_granularity;
83	} else {
84		if (cap & PCI_PTM_CAP_ROOT) {
85			ctrl = PCI_PTM_CTRL_ENABLE | PCI_PTM_CTRL_ROOT;
86			dev->ptm_root = 1;
87			dev->ptm_granularity = local_clock;
88		} else
89			return;
90	}
91
92	ctrl |= dev->ptm_granularity << 8;
93	pci_write_config_dword(dev, pos + PCI_PTM_CTRL, ctrl);
94	dev->ptm_enabled = 1;
95
96	pci_ptm_info(dev);
97}
98
99int pci_enable_ptm(struct pci_dev *dev, u8 *granularity)
100{
101	int pos;
102	u32 cap, ctrl;
103	struct pci_dev *ups;
104
105	if (!pci_is_pcie(dev))
106		return -EINVAL;
107
108	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_PTM);
109	if (!pos)
110		return -EINVAL;
111
112	pci_read_config_dword(dev, pos + PCI_PTM_CAP, &cap);
113	if (!(cap & PCI_PTM_CAP_REQ))
114		return -EINVAL;
115
116	/*
117	 * For a PCIe Endpoint, PTM is only useful if the endpoint can
118	 * issue PTM requests to upstream devices that have PTM enabled.
119	 *
120	 * For Root Complex Integrated Endpoints, there is no upstream
121	 * device, so there must be some implementation-specific way to
122	 * associate the endpoint with a time source.
123	 */
124	if (pci_pcie_type(dev) == PCI_EXP_TYPE_ENDPOINT) {
125		ups = pci_upstream_bridge(dev);
126		if (!ups || !ups->ptm_enabled)
127			return -EINVAL;
128
129		dev->ptm_granularity = ups->ptm_granularity;
130	} else if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) {
131		dev->ptm_granularity = 0;
132	} else
133		return -EINVAL;
134
135	ctrl = PCI_PTM_CTRL_ENABLE;
136	ctrl |= dev->ptm_granularity << 8;
137	pci_write_config_dword(dev, pos + PCI_PTM_CTRL, ctrl);
138	dev->ptm_enabled = 1;
139
140	pci_ptm_info(dev);
141
142	if (granularity)
143		*granularity = dev->ptm_granularity;
144	return 0;
145}
146EXPORT_SYMBOL(pci_enable_ptm);
147